jshmrtn / vue3-gettext

Translate Vue 3 applications with gettext.
https://jshmrtn.github.io/vue3-gettext/
MIT License
66 stars 23 forks source link

Lazy load language files to minimize download size #57

Open jbaldivieso opened 1 month ago

jbaldivieso commented 1 month ago

It appears that, despite having specified splitJson: true in the config, ultimately all translations get minified into the same file for my app. It would be ideal to lazily load languages only upon their activation. Once you have multiple languages with nontrivial amounts of content, the file size really starts to add up, with no benefit to your average single-language user.

FlorianWerndl commented 1 month ago

@jbaldivieso it may not fit to your needs/setup but I implemented it myself. I have a nuxt application where gettext is initialized within a plugin and if I change the language the whole application is reloaded (I guess this is needed for this approach)

// plugins/translate.ts
await gettext(nuxtApp.$i18n.locale.value)

// gettext.js
...
export default async function gettext(lang) {
  const languageFile = await import(`../locales/dist/${lang}.json`);
  return createGettext({
    availableLanguages,
    silent: true,
    defaultLanguage: lang,
    translations: languageFile,
    provideDirective: false,
    provideComponent: false,
  });
}
jbaldivieso commented 1 month ago

@FlorianWerndl–Thank you very much for sharing this! I took a closer look at the source code, and it appears that createGettext stores translations as a reactive variable, complete with a getter and setter. So one is able to hot-swap the translation file on the fly (dynamically importing it as you suggest) without having to reload the app.

Since this doesn't seem like an edge case (right? the average user only wants an app/website in a single language), I think this process could be smoother/better documented.

Here is what worked for me:

const availableLanguages = {
  en: "English",
  es: "Español",
  fr: "Francais",
}
const translations = {
  // This is fake; upon initial load, we call `updateLanguage` below to import
  // a *real* translation file. This prevents us from downloading the
  // English translation if that's not our browser's preferred language.
  en: { en: { stub: "stub" } }
}

export const gettext = createGettext({
  defaultLanguage: "en",
  availableLanguages,
  translations,
})

export async function updateLanguage(lang: string) {
  // Guard here against possible crazy imports
  if (lang in availableLanguages) {
    const trans = await import(`@/locales/translations/${lang}.json`)
    gettext.translations = trans
    gettext.current = lang
  }
}

And then when the app mounts, I call logic that looks up/infers the language (cookie, local storage, request headers) and calls updateLanguage asynchronously to set translations to a meaningful translation file. (In my case, I'm using code splitting with manually chunks in rollup, so I have to make sure that each translation file is represented there in a separate chunk.)