sveltejs / kit

web development, streamlined
https://svelte.dev/docs/kit
MIT License
18.74k stars 1.95k forks source link

i18n brainstorming #553

Open Rich-Harris opened 5 years ago

Rich-Harris commented 5 years ago

We've somewhat glossed over the problem of internationalisation up till now. Frankly this is something SvelteKit isn't currently very good at. I'm starting to think about how to internationalise/localise https://svelte.dev, to see which parts can be solved in userland and which can't.

(For anyone unfamiliar: 'Internationalisation' or i18n refers to the process of making an app language agnostic; 'localisation' or l10n refers to the process of creating individual translations.)

This isn't an area I have a lot of experience in, so if anyone wants to chime in — particularly non-native English speakers and people who have dealt with these problems! — please do.

Where we're currently at: the best we can really do is put everything inside src/routes/[lang] and use the lang param in preload to load localisations (an exercise left to the reader, albeit a fairly straightforward one). This works, but leaves a few problems unsolved.

I think we can do a lot better. I'm prepared to suggest that SvelteKit should be a little opinionated here rather than abdicating responsibility to things like i18next, since we can make guarantees that a general-purpose framework can't, and can potentially do interesting compile-time things that are out of reach for other projects. But I'm under no illusions about how complex i18n can be (I recently discovered that a file modified two days ago will be labeled 'avant-hier' on MacOS if your language is set to French; most languages don't even have a comparable phrase. How on earth do you do that sort of thing programmatically?!) which is why I'm anxious for community input.


Language detection/URL structure

Some websites make the current language explicit in the pathname, e.g. https://example.com/es/foo or https://example.com/zh/foo. Sometimes the default is explicit (https://example.com/en/foo), sometimes it's implicit (https://example.com/foo). Others (e.g. Wikipedia) use a subdomain, like https://cy.example.com. Still others (Amazon) don't make the language visible, but store it in a cookie.

Having the language expressed in the URL seems like the best way to make the user's preference unambiguous. I prefer /en/foo to /foo since it's explicit, easier to implement, and doesn't make other languages second-class citizens. If you're using subdomains then you're probably running separate instances of an app, which means it's not SvelteKit's problem.

There still needs to be a way to detect language if someone lands on /. I believe the most reliable way to detect a user's language preference on the server is the Accept-Language header (please correct me if nec). Maybe this could automatically redirect to a supported localisation (see next section).

Supported localisations

It's useful for SvelteKit to know at build time which localisations are supported. This could perhaps be achieved by having a locales folder (configurable, obviously) in the project root:

locales
|- de.json
|- en.json
|- fr.json
|- ru.json
src
|- routes
|- ...

Single-language apps could simply omit this folder, and behave as they currently do.

lang attribute

The <html> element should ideally have a lang attribute. If SvelteKit has i18n built in, we could achieve this the same way we inject other variables into src/template.html:

<html lang="%svelte.lang%">

Localised URLs

If we have localisations available at build time, we can localise URLs themselves. For example, you could have /en/meet-the-team and /de/triff-das-team without having to use a [parameter] in the route filename. One way we could do this is by encasing localisation keys in curlies:

src
|- routes
   |- index.svelte
   |- {meet_the_team}.svelte

In theory, we could generate a different route manifest for each supported language, so that English-speaking users would get a manifest with this...

{
  // index.svelte
  pattern: /^\/en\/?$/,
  parts: [...]
},

{
  // {meet_the_team}.svelte
  pattern: /^\/en/meet-the-team\/?$/,
  parts: [...]
}

...while German-speaking users download this instead:

{
  // index.svelte
  pattern: /^\/de\/?$/,
  parts: [...]
},

{
  // {meet_the_team}.svelte
  pattern: /^\/de/triff-das-team\/?$/,
  parts: [...]
}

Localisation in components

I think the best way to make the translations themselves available inside components is to use a store:

<script>
  import { t } from '$app/stores';
</script>

<h1>{$t.hello_world}</h1>

Then, if you've got files like these...

// locales/en.json
{ "hello_world": "Hello world" }
// locales/fr.json
{ "hello_world": "Bonjour le monde" }

...SvelteKit can load them as necessary and coordinate everything. There's probably a commonly-used format for things like this as well — something like "Willkommen zurück, $1":

<p>{$t.welcome_back(name)}</p>

(In development, we could potentially do all sorts of fun stuff like making $t be a proxy that warns us if a particular translation is missing, or tracks which translations are unused.)

Route-scoped localisations

We probably wouldn't want to put all the localisations in locales/xx.json — just the stuff that's needed globally. Perhaps we could have something like this:

locales
|- de.json
|- en.json
|- fr.json
|- ru.json
src
|- routes
   |- settings
      |- _locales
         |- de.json
         |- en.json
         |- fr.json
         |- ru.json
      |- index.svelte

Again, we're in the fortunate position that SvelteKit can easily coordinate all the loading for us, including any necessary build-time preparation. Here, any keys in src/routes/settings/_locales/en.json would take precedence over the global keys in locales/en.json.

Translating content

It's probably best if SvelteKit doesn't have too many opinions about how content (like blog posts) should be translated, since this is an area where you're far more likely to need to e.g. talk to a database, or otherwise do something that doesn't fit neatly into the structure we've outlined. Here again, there's an advantage to having the current language preference expressed in the URL, since userland middleware can easily extract that from req.path and use that to fetch appropriate content. (I guess we could also set a req.lang property or something if we wanted?)

Base URLs

Sapper (ab)used the <base> element to make it easy to mount apps on a path other than /. <base> could also include the language prefix so that we don't need to worry about it when creating links:

<!-- with <base href="de">, this would link to `/de/triff-das-team` -->
<a href={$t.meet_the_team}>{$t.text.meet_the_team}</a>

Base URLs haven't been entirely pain-free though, so this might warrant further thought.


Having gone through this thought process I'm more convinced than ever that SvelteKit should have i18n built in. We can make it so much easier to do i18n than is currently possible with libraries, with zero boilerplate. But this could just be arrogance and naivety from someone who hasn't really done this stuff before, so please do help fill in the missing pieces.

bbuhler commented 3 years ago

@xpuu since you are looking into generate a static site, with SEO focus, rendered by Svelte, I can recommend Elder.js. There is also a community plugin for i18n.

jaybytez commented 3 years ago

Totally new to sapper, but just give some context for the Next.js mentions. For us its difficult because it's i18n support does not support its 'next export' feature for full static site generation, it doesn't support getting the locale with its basePath feature needed to run the app as a subdomain, and currently hitting build concerns because we support 100+ languages and therefore some SSG pages will attempt to build all the language variations at build time instead of support some locales and build time and some through their fallback feature.

floratmin commented 3 years ago

Made some more research into this topic. For i18n to be really svelte we should consider that the most seldom used functionality is to switch a language. Once a user has chosen his language he will almost never change it again. This means that we should never load all available translations in one file. I would even separate each language version from one another completely. I am looking at the moment at the messageformat package (where v3 is coming out soon). They provide compilers that compile the ICU strings into pure javascript functions (separate for each language), which can, depending on the requirements, be embedded or resolved.

ehsanonline commented 3 years ago

This can be useful: Using i18Next with Svelte

floratmin commented 3 years ago

I investigated the lifecycle for translations made with gettext. I think the approach for message strings is most elegant with messageformat. But the tooling and workflow are best with gettext. I was thinking if we could just use the message strings from messageformat as the msgid in gettext. So we could get the benefit from both approaches. I am at the moment experementing with the library gettext-extractor to generate a complete lifecycle for extracting and generating the required files. This lifecycle needs a lot of additional information like used languages, URL for the newest untranslated live version of the app, translation teams, and their mailing lists, information about translators, their languages, and their email address, email address for reporting bugs… An additional benefit of gettext is that there are a lot of compendia that could be used in open source projects (e.g. Debian Compendia).

stalkerg commented 3 years ago

Once a user has chosen his language he will almost never change it again.

not really :(

andreasnuesslein commented 3 years ago

Made some more research into this topic. For i18n to be really svelte we should consider that the most seldom used functionality is to switch a language. Once a user has chosen his language he will almost never change it again.

I also disagree with this. Right now for example, we have a customer that needs a lot of language switching.

pngwn commented 3 years ago

Not to mention that not all language variants of a document are created equal. Sometimes a user will switch to a different language for better content due to discrepancies between languages, and sometimes some content isn't translated at all in which case a fallback of some description is needed.

floratmin commented 3 years ago

There are use cases for switching languages more often, but I think for most applications this is not necessary and would result in downloading a lot of extra files which will very seldom be used. If necessary service workers could keep previously downloaded language files, or there could be some kind of extra option for other strategies.

Cochonours commented 3 years ago

A good default strategy could be to download the language files associated with the languages set in the navigator ( navigator.languages ) after the initial page is fully loaded and ready.

stalkerg commented 3 years ago

It's should be with async load and at the same time possible for preload specific language.

rbenzazon commented 3 years ago

Runtime language update should be an option in my opinion. Maybe, considering the compiled nature of svelte, the code handling this feature would only be outputed where it's being used

On Sat, May 1, 2021, 08:22 Yury Zhuravlev @.*** wrote:

It's should be with async load and at the same time possible for preload specific language.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/sveltejs/kit/issues/553#issuecomment-830562676, or unsubscribe https://github.com/notifications/unsubscribe-auth/ACLIE5FZSS2USJYSXDDOGK3TLOMZXANCNFSM4ZJLG5NQ .

lsabi commented 3 years ago

In my opinion, language switches are not so frequent, but can still occur from time to time (a lot depends on the type of website and its content).

If a user switches language, then he may wait some time for svelte to switch language, downloading the translations and rendering again the components, but with the new language. Also, if there's dynamic content (i.e. fetched from a server) this will likely be fetched again, but in another language.

A good strategy is then to assume that when a user starts with a language, a switch can occur, but then the app has to reload and prepare again the page by fetching the translations and content in a different language. That's my point of view. Also, a fallback language is needed, just in case.

maelp commented 3 years ago

Is there a way to automatically rewrite all links in a page to include a queryString like ?lang=en so that if a user goes to my website, and loads example.com/page?lang=en all the links to other pages will dynamically be rewritten with a ?lang=en appended? This would help me solve a bit my i18n issue without having to resort to route rewriting (I'd like to keep the website as static on github).

dreitzner commented 3 years ago

As some of the projects in the future at the company I work at might need i18n I thought I jump into the discussion :)

As we will be using a headless CMS and would aim for prerendering all pages, here are my two cents.

For us the most practical way would be to have something similar to https://i18n.nuxtjs.org/. So something like that in the config

/** @type {import('@sveltejs/kit').Config} */
const config = {
    // all the other stuff

    // options passed to svelte.preprocess (https://svelte.dev/docs#svelte_preprocess)
    preprocess: {
            i18n: {
                locales: ['en', 'de', 'fr', 'es'],
                defaultLocale: 'en',
            }
};

export default config;

With that we could extend the load function for SSR:

<script context="module">
    /**
     * @type {import('@sveltejs/kit').Load}
     */
    export async function load({ page, fetch, session, context, lang }) {
        const url = `/blog/${lang}/${page.params.slug}.json`; (or some headless CMS)
        const res = await fetch(url);

        if (res.ok) {
            return {
                props: {
                    article: await res.json()
                }
            };
        }

        return {
            status: res.status,
            error: new Error(`Could not load ${url}`)
        };
    }
</script>

This should create something like

/article/{id}
/de/article/{id}
/fr/article/{id}
/es/article/{id}

The advantage of this pattern would be that it is extendable through configuration. It could be extended by using cookies, or query params (not very crawlable, but possible) in the future. And it would leave a lot of options on how to handle language retrieval if the lang is accessible inside of the load function.

dreitzner commented 3 years ago

Added a little prototype that should work with a load function, so it will not work with a static export I guess.... I am using:

repo

dreitzner commented 3 years ago

Ok, after some work, I have a working POC where svelte kit works the magic itself. I made a draft PR: https://github.com/sveltejs/kit/pull/1810

icalvin102 commented 3 years ago

As many before me said: I think we should not reinvent the wheel in regards to the message format. svelte-i18n and svelte-intl-precompile already provide very nice ways to use the ICU message format in svelte.

I think svelte-intl-precompile especially is a good source of inspiration as it embraces the compiled nature of svelte.

ICU format that gets compiled to javascript at build could serve as sveltekit default format as it's quite powerful. And if a user needs support for other formats like PO they could write a po2icu preprocessor for this.

dreitzner commented 3 years ago

@icalvin102 I think what you reference better fits with #1274.

This issue is more about the language itself and not the translation once the language is available.

You can check out my PR.

constantant commented 3 years ago

Hi @Rich-Harris, As an Angular developer I find the Angular approach quite useful. Please take a look at this in docs https://angular.io/guide/i18n

The key advantage of the API is that you do not need to care about the silly keys like $_('home.topic') (which means nothing and take a lot of efforts to develop and maintain it).

In addition I would like to see some CLI that allows to extract all translations and provide it as POT-file(gettext) and apply PO-file as a translation source for creating the localized UI.

ansbr commented 3 years ago

Hello everyone, I love the svelte kit, and I am using it in my projects, and I think we can do i18n better with small losses. For example, the easiest way to make i18n working is:

http://example.com/about
http://example.com/about?lang=de
http://example.com/about?lang=es

That's really simple, everything you need is to install svelte-i18n or svelte-intl-precompile and write a little code.

BUT, in one of my not Svelte projects I using structures like:

http://example.com/about
http://example.com/de/about
http://example.com/es/about

And now the svelte kit is not ready for that structure, because I need to make clones of my components in a subfolder like this:

routes/
  [lang]/
    about.svelte
  about.svelte

That's not what I really want, another way without clones is using path routes/[lang]/about.svelte, but in that way, I can't hide en in URLs, so I have that one:

http://example.com/en/about
http://example.com/de/about
http://example.com/es/about

When I read documentation, I found hooks. And I think I will happy, with using that code:

const i18n = {
  locales: ['en', 'ru', 'de', 'es'],
  defaultLocale: 'en',
}

export const handle = async ({ request, resolve }) => {
  let lang = i18n.defaultLocale;
  let path = request.path;

  i18n.locales.forEach(locale => {
    const regex = new RegExp(^\/${locale}\/?);
    if (path.match(regex)) {
      lang = locale;
      path = path.replace(regex, '/');

    }
  });
  request.path = path;
  request.locals.lang = lang;

  const response = await resolve(request);
  return response;
};

export function getSession(request) {
  return {
    lang: request.locals.lang || i18n.defaultLocale,
    path: request.path
  };
}

I thought that's all, I win, and the svelte kit is perfect for me, but I was wrong. When I start yarn dev with the router: true inside svelte.config.js I saw 404 when clicking on the i18n page (but after refreshing the page, it was ok). Then I disable the frontend router router: false, and it`s was working, but without benefits.

So, I think we can do i18n better with small losses, just making feature to extend manifest, for using frontend router. I don't know how to do it, but maybe @Rich-Harris or @benmccann knows? If it would possible to change manifest inside svelte.config.js, we could change from this:

[/^\/$/],
[/^\/about\/?$/]

to something like this:

[/^\/(\S{2}\/)?$/],
[/^\/(\S{2}\/)?about\/?$/]

If you make some extending feature for hooks, manifest, and attr to HTML tag, the community could make a plugin for i18n for the svelte kit, without adding i18n to the core. It would be perfect for everyone.

andreasnuesslein commented 3 years ago

@tonborzenko I think what you're saying about the router might be helpful to some extent but it doesn't really solve all the cases.

For one, I don't particularly like the assumption that English has to be the only first level citizen (I think that has also been mentioned here somewhere already). But granted that's a matter of taste, not a technical issue and things would become easier with only having /<lang>/about and not the extra /about.

However number two: It seems, you assume that "about" will always be English? I definitely have cases where it's rather supposed to be /en/about, /de/ueber-uns, /es/quienes-somos or similar.

Cochonours commented 3 years ago

However number two: It seems, you assume that "about" will always be English? I definitely have cases where it's rather supposed to be /en/about, /de/ueber-uns, /es/quienes-somos or similar.

Exactly. Being able to translate the paths is essential, along with an easy way to populate a menu allowing the user to jump into other translations.

Personally though, I think "/about" should be English, "/quienessomos" Spanish and etc. So the language tag could be optional and in fact only necessary to discriminate when the path is not sufficient.

andreasnuesslein commented 3 years ago

Personally though, I think "/about" should be English, "/quienessomos" Spanish and etc. So the language tag could be optional and in fact only necessary to discriminate when the path is not sufficient.

That sounds like it's gonna be confusing very quickly. Just off the top of my head: /method vs /methode (English v German) is very similar and I might accidentally grab the wrong one. And then for the same words, like gold, you'd have /en/gold and /de/gold but only for them? But I guess that's personal taste, true.

Cochonours commented 3 years ago

Exactly, the language tag is redundant for most addresses as the paths are clear. Also if you mistype (which isn't a big problem as most people don't type in addresses) and you stumble upon the wrong translation it's not a big problem as there should be a translation menu on every page allowing to go to available translations.

fabian-michael commented 3 years ago

Would it somehow be possible to have an i18n option in the svelte config where you can define locales and then have some kind of file name syntax you can use to define multiple names for a route in the same order as you defined the locales?

E.g. you define the following locales ['en', 'de'] and have a route like (about|ueber-uns).svelte or in the case of "gold" you could just use gold.svelte

andreasnuesslein commented 3 years ago

@fabian-michael the naming thing won't scale at all. :S and you'd run into all kinds of trouble getting the order right. let's say you have 5 languages, it's already bananas. but with 10? "which language comes at position 8 again"?

The best idea I can come up with is: stay in your language, if you code everything English, just write the stuff in English (about.svelte), if you have a German project, just call the file German in the beginning. Then in the context="module" block, let the magic happen. maybe have tags similar to export ssr = false; but like export router_names = {'de':...}; maybe the async function load() could return a dictionary with the correct paths, this way it's easier to first fetch names from an API or some place.

This way you can just solve it on a file to file basis, if you like that, or you can defer within every load function (or the __layout.svelte's load function) to a routine that will solve it for all your routes.

One way or another, this is a REALLY hard problem, if you want to keep flexibility so people can go different ways regarding their routing names and also make it easy/nice to handle. And I think, just like the svelte router is opinionated, there should probably a default solution that works for most people.

fabian-michael commented 3 years ago

Yeah makes sense. I like the idea of the exported field in the context="module" block.

lsabi commented 3 years ago

Let's not forget that there are also dynamic routes. Take as example an ecommerce like amazon. If you search, you'll find in the url the slug for the product based on the current locale. This cannot be hardcoded.

An option could be to show to the user a url, but actually navigate to a different one. If I'm not mistaken, that's the key idea behind Svelte's navigation: change the url displayed on the browser. The translation could be placed inside the script context="module", where a simple lookup translates the name about to uber uns (from en to de) or simply keeps about because the translation is missing for the desired language. In the case of a dynamic route, the translation could be checked inside the preload function

stalkerg commented 3 years ago

The solution should be agnostic to way getting language value. In my case, it's a GET argument.

Cochonours commented 3 years ago

Translating the path is not a good idea, SEO need also to be taking into account to avoid duplicate content. It should be like this:

/about (en,default,canonical) /en/about (en,alternate) /fr/about (fr,alternate)

This is so wrong. There are several ways to encode the language tag into the url, and your example is just one of those. https://developers.google.com/search/docs/advanced/crawling/managing-multi-regional-sites

Also, there are ways to tell search engines robots which pages are translations of which others, and in no cases do they require part of their paths to look the same! This info should be in the html, and the user doesn't have to bear with paths written in a foreign language.

There is no point in having your domain name translated if you have to keep parts of the urls in english. By all means you are welcome to do that for your websites, but if svelte cannot handle more by default then it is better not to do anything.

ecstrema commented 3 years ago

I have been working on a template that implements a few of the features described in this thread.

Have a look at it and tell me what you think

chavdar-ang commented 2 years ago

Do you think it's possible to just have routes that act as a parameter? For example prepender (/fr/about) or appended (/posts/newest). They can act as regular query params but can look like routes so they don't mess with the folder structure routes. They can be defined in a json file inside the routes folder where they should be applied. For example:

src/routes/[lang].json

{
    type: "prepend",
    optional: true, // maybe always optional
    default: "en",
    enum: ["en", "fr", "de", "es"]
}

src/routes/posts/[sort].json

{
    type: "append",
    default: "latest",
    enum: ["latest", "first"]
}
stalkerg commented 2 years ago

Also, we shouldn't forget about statically generated pages, because my current implementation it's dynamic and I can't use static generated pages anymore.

jbergstroem commented 2 years ago

typesafe-i18n is a library that folds very well into a Sveltekit/Svelte project. The locales format looks new (read: no tight integration with translation sites) but I really enjoyed the tight typescript integration. There are examples of a Svelte setup to get you going.

Just wanted to mention it here if other people are looking for options in the space.

zummon commented 2 years ago

The basic and for static build, may not well complete. But I hope it will do and give you ideas to begin.

Default language is English en, other languages are es, ... Index path or home page will look like /en not /

Folder structure & files

Paths result

This setting should have, route / to /*default lang* as home page

I use Firebase hosting to deploy. I think you can do redirects for other hosting providers as well. I set redirects there for - I think it's required, and an example to set in *firebase.json* - `/` to `/en` ```json { "hosting": { ... "redirects": [ { "source": "/", "destination": "/en", "type": 301 } ] } } ``` - After this I think it's optional - `/about` to `/en/about` - `/blog/**` to `/en/blog/**` or whatever wildcard rule is When `npm run dev` still need to manually enter to `http://localhost:3000/en` Or I think you set **SvelteKit** re-routes for this situation. I don't yet implement.

Pages data included translations Create new `$lib/content.js` to contain all data you may need ```js export default { "": { // home page "en": { "title": "title", // input all translations for the page here }, "es": { "title": "título", // input all translations for the page here }, }, "about": { // about page "en": { "title": "about", }, "es": { "title": "acerca de", }, }, "blog/nature": { // blog/nature page "en": { "title": "nature blog", }, "es": { "title": "...", }, }, ... } ```
Set svelte.config.js for dynamic route/[lang]

```js import content from "./src/lib/content.js"; const pathnames = Object.entries(content).flatMap(([slug, langobj]) => { return Object.keys(langobj).map((lang) => `/${lang}/${slug}`) }) const config = { kit: { ... prerender: { entries: pathnames, }, }, }; ``` I'm not sure it can do something like `/en/*`, `/es/*` as dynamic routes for only `/[lang]/*` Also I didn't implement like `../routes/[lang]/blog/[slug].svelte` **slug** here.

{t.title} ... {#each langs as lng} {lng} {/each} ``` That's a lot already. I'm not sure if something is missing and may lead to errors, let's ask and I'll be learning as well.
jarda-svoboda commented 2 years ago

Hi, i just released v1.0.0 of my i18n library for SvelteKit – https://github.com/jarda-svoboda/sveltekit-i18n

It includes features like:

Let me know if it could be somehow useful for you...

ivanhofer commented 2 years ago

@jbergstroem thanks for mentioning typesafe-i18n.

I have also prepared another example that demonstrates what is currently needed to create a good i18n experience with SvelteKit: https://github.com/ivanhofer/typesafe-i18n-demo-sveltekit

The most important parts are located in these files:

This example uses typesafe-i18n but you can replace it with another i18n library if you like.

benmccann commented 2 years ago

Great to see people making progress with i18n! Here's another cool library I've seen for handling translations in Svelte: https://github.com/cibernox/svelte-intl-precompile

vhscom commented 2 years ago

I like this concept. Would it be appropriate to work this into the kit now so userland can start integrating it and the kit can then iterate towards other items outlined by Rich in early 2019?

<html lang="%svelte.lang%">
ivanhofer commented 2 years ago
<html lang="%svelte.lang%">

Until this gets implemented, you could use this workaround inside your hooks file.

bfanger commented 2 years ago

@ivanhofer Thanks for your talk on i18n in SvelteKit! https://youtu.be/9_LoEY8hO44?t=1990 (Svelte Summit Mini Edition)

ivanhofer commented 2 years ago
<html lang="%svelte.lang%">

Until this gets implemented, you could use this workaround inside your hooks file.

This has changed in a recent release of SvelteKit, now you need to write it like this.

@bfanger thanks for posting the link to my i18n talk!

svenjacobs commented 2 years ago

Although I would prefer an official i18n solution in SvelteKit, I just integrated typesafe-i18n. Setup was pretty straightforward, following the SvelteKit example project. I like it 👍🏼

evgenyfadeev commented 2 years ago

Hi guys, I've been following this thread for a while, please consider allowing extraction of strings to .po files. Please think about the translators too, it would be great to be able to take advantage of the tools like Transifex, otherwise your translation strings will be inaccessible to the translators.

Catsvilles commented 2 years ago

Although I would prefer an official i18n solution in SvelteKit, I just integrated typesafe-i18n. Setup was pretty straightforward, following the SvelteKit example project. I like it 👍🏼

Cool, but how would you go with not having the language code in the URL for the default language? Looks like all Svelte i18n projects struggle with this same problem :)

svenjacobs commented 2 years ago

Cool, but how would you go with not having the language code in the URL for the default language? Looks like all Svelte i18n projects struggle with this same problem :)

To be honest, I haven't thought about this yet. Currently I don't have any language code in the URL. I read the Accept-Language header in the OnLoad hook, map it to an available localization ( typesafe-i18n provides helpers for this) and write the current locale to the session.

ivanhofer commented 2 years ago

Cool, but how would you go with not having the language code in the URL for the default language? Looks like all Svelte i18n projects struggle with this same problem :)

I would not say "struggle". It is a choice you can make. See this SO post for some arguments why you should also include the default language in your url.

It's not really the fault of the i18n library. It's more the current limitation of SvelteKit that does not support it in an easy way. You could also use a routes/[...path].svelte route as a single entry to your application and then detect the locale from there and render the page you want. Or disable the SvelteKit router and use something like Routify, which seems to support your usecase.

ivanhofer commented 2 years ago

To be honest, I haven't thought about this yet. Currently I don't have any language code in the URL. I read the Accept-Language header in the OnLoad hook, map it to an available localization ( typesafe-i18n provides helpers for this) and write the current locale to the session.

It's a good experience to auto-detect the locale when a user first visits your page, but you should also offer the option to switch locale. A few years ago the fitbit.com website detected the users locale via IP address. You could change the locale via the url, but once you have clicked on a link, you would immediately get redirected to the other locale again. This was a really frustrating experience. Auto-detection will work for 95% of the cases but there should always be an easy option to change it.

svenjacobs commented 2 years ago

It's a good experience to auto-detect the locale when a user first visits your page, but you should also offer the option to switch locale. A few years ago the fitbit.com website detected the users locale via IP address. You could change the locale via the url, but once you have clicked on a link, you would immediately get redirected to the other locale again. This was a really frustrating experience. Auto-detection will work for 95% of the cases but there should always be an easy option to change it.

I get your point, however Accept-Language is a value that can be controlled by the user (through a browser setting) in contrast to the IP address, where the region of the user is just guessed and which has probably no relation to the language the user is speaking. But I believe we're getting off topic here 😉