kaisermann / svelte-i18n

Internationalization library for Svelte
MIT License
1.26k stars 80 forks source link

Translations still not loaded in after waitLocale() #142

Closed skoshx closed 3 years ago

skoshx commented 3 years ago

Describe the bug In SvelteKit's load() function, I await waitLocale(), and then proceed to render. Even though i wait for the locale's to be loaded before rendering the page, it renders using the fallback locale, then quickly changes to the actual wanted locale. This is not expected behaviour, as I don't want users to get a flash of text in some random language they don't speak, before rendering in their desired language.

Logs No errors, just unexpected behaviour.

To Reproduce

<script context="module" lang="ts">

  import { addMessages, init, getLocaleFromNavigator, waitLocale, _ } from "svelte-i18n";
  import en from "$lib/i18n/en.json";
  import fi from "$lib/i18n/fi.json";
  addMessages("en", en);
  addMessages("en-US", en);
  addMessages("fi", fi);

  init({
    fallbackLocale: 'fi',
    initialLocale: getLocaleFromNavigator(),
  });

  export async function load() {
    await waitLocale();
    return {};
  }

</script>

<h1>{$_("page.home.title")}</h1> <!-- This will first render in the fallback language, then in the right one. -->
<slot />

Expected behavior After awaiting waitLocale() the page should render with the wanted locale, initialLocale: getLocaleFromNavigator() instead of fallbackLocale first, and then quickly after initialLocale.

Stacktraces None

Information about your project:

Additional context None

bztes commented 3 years ago

If you are using SvelteKit you probably has SSR enabled which is way you seeing this issue: getLocaleFromNavigator() won't work on the server side. Therefor the page will be rendered with the default locale and then later updated by the client with whatever language the browser returns. Simple workaround would be to disable SSR. If SSR should stay enabled you have other options to get the locale on server side, e.g. reading the "accept-language" header, use Cookies or parse the URL

skoshx commented 3 years ago

@bztes Ok, that actually makes sense, however, I think that getLocaleFromNavigator() should throw an error if navigator isn't defined, that way it is clear, that it cannot be used on the server side (in hindsight, it's obvious) and doesn't just fail silently, prompting to think that its a bug or something…

TimKieu commented 6 months ago

@skoshx @bztes We can using a trick like this code in a Selector component:

<script>
    import { Select } from 'flowbite-svelte';
    import { setupI18n } from "$lib/translations/i18n"; 
    import languages from "$lib/translations/languages"; 
    import { browser } from "$app/environment"; 

    //1. Props passing assume <LanguageDropdown selectedLang ="en" /> in Navbar
    export let selectedLang = browser
        ? localStorage.getItem("I18N_LANGUAGE") || "en"
        : "en"; 

    function handleLocaleChange(lang) {
        setupI18n({ withLocale: lang });
        if (browser) {
            localStorage.setItem("I18N_LANGUAGE", lang);
        } 
    }

    //2. Then onMounted, we update to our target expected lang without concerning SSR/CSR again
    if (browser) {
            selectedLang = "vi";
            handleLocaleChange("vi");
    }  
</script>

<div class="flex rounded-full  "> 
<Select id="countries" class="mt-2" bind:value={selectedLang} placeholder=""
    on:change={() => handleLocaleChange(selectedLang)}
    >

  {#each languages as language }
        {#if language.value===selectedLang }
            <option selected value={selectedLang} id={language.value}>
                {language.label}
            </option>
        {:else}
            <option value={language.value} id={language.value} >
                {language.label}
        </option>
        {/if}
  {/each}

</Select>
</div> 

Pretty awesome compare to complicated sveltkit-i18n when we must prepare and process much on SSR code and CSR code.

TimKieu commented 6 months ago

Another dump solution is that using your target locale as all keys in .json translation files.