Skip to content

Commit

Permalink
(web): lazy-load languages
Browse files Browse the repository at this point in the history
  • Loading branch information
cstenglein committed Nov 10, 2024
1 parent 074e758 commit 8ac388b
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 62 deletions.
7 changes: 4 additions & 3 deletions frontends/web/src/components/language/language.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { Dialog } from '@/components/dialog/dialog';
import { defaultLanguages, TActiveLanguageCodes, TLanguagesList } from './types';
import style from './language.module.css';
import { getSelectedIndex } from '@/utils/language';
import { changei18nLanguage } from '@/i18n/i18n';

type TLanguageSwitchProps = {
languages?: TLanguagesList;
Expand All @@ -34,10 +35,10 @@ const LanguageSwitch = ({ languages }: TLanguageSwitchProps) => {
const [selectedIndex, setSelectedIndex] = useState<number>(getSelectedIndex(allLanguages, i18n));
const [activeDialog, setActiveDialog] = useState<boolean>(false);

const changeLanguage = (langCode: TActiveLanguageCodes, index: number) => {
const changeLanguage = async (langCode: TActiveLanguageCodes, index: number) => {
setSelectedIndex(index);
setActiveDialog(false);
i18n.changeLanguage(langCode);
await changei18nLanguage(langCode);
};

if (allLanguages.length === 1) {
Expand Down Expand Up @@ -106,4 +107,4 @@ const LanguageSwitch = ({ languages }: TLanguageSwitchProps) => {
);
};

export { LanguageSwitch };
export { LanguageSwitch };
118 changes: 60 additions & 58 deletions frontends/web/src/i18n/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,36 +17,58 @@

import i18n from 'i18next';
import { getNativeLocale } from '@/api/nativelocale';
import appTranslationsAR from '@/locales/ar/app.json';
import appTranslationsCS from '@/locales/cs/app.json';
import appTranslationsDE from '@/locales/de/app.json';
import appTranslationsEN from '@/locales/en/app.json';
import appTranslationsFR from '@/locales/fr/app.json';
import appTranslationsJA from '@/locales/ja/app.json';
import appTranslationsRU from '@/locales/ru/app.json';
import appTranslationsMS from '@/locales/ms/app.json';
import appTranslationsNL from '@/locales/nl/app.json';
import appTranslationsPT from '@/locales/pt/app.json';
import appTranslationsHI from '@/locales/hi/app.json';
import appTranslationsBG from '@/locales/bg/app.json';
import appTranslationsTR from '@/locales/tr/app.json';
import appTranslationsZH from '@/locales/zh/app.json';
import appTranslationsFA from '@/locales/fa/app.json';
import appTranslationsES from '@/locales/es/app.json';
import appTranslationsSL from '@/locales/sl/app.json';
import appTranslationsHE from '@/locales/he/app.json';
import appTranslationsIT from '@/locales/it/app.json';
import { languageFromConfig } from './config';
import { localeMainLanguage } from './utils';
import { setConfig } from '@/utils/config';

const locizeProjectID = 'fe4e5a24-e4a2-4903-96fc-3d62c11fc502';

let i18Init = i18n
.use(languageFromConfig);
const defaultLang = 'en';

const languageResources = {
ar: () => import('@/locales/ar/app.json'),
cs: () => import('@/locales/cs/app.json'),
de: () => import('@/locales/de/app.json'),
en: () => import('@/locales/en/app.json'),
fr: () => import('@/locales/fr/app.json'),
ja: () => import('@/locales/ja/app.json'),
ru: () => import('@/locales/ru/app.json'),
ms: () => import('@/locales/ms/app.json'),
nl: () => import('@/locales/nl/app.json'),
pt: () => import('@/locales/pt/app.json'),
hi: () => import('@/locales/hi/app.json'),
bg: () => import('@/locales/bg/app.json'),
tr: () => import('@/locales/tr/app.json'),
zh: () => import('@/locales/zh/app.json'),
fa: () => import('@/locales/fa/app.json'),
es: () => import('@/locales/es/app.json'),
sl: () => import('@/locales/sl/app.json'),
he: () => import('@/locales/he/app.json'),
it: () => import('@/locales/it/app.json')
};

type LanguageKey = keyof typeof languageResources;

export const loadLanguage = async (language: string) => {
try {
const resources = await languageResources[language as LanguageKey]();
if (!i18n.hasResourceBundle(language, 'app')) {
i18n.addResourceBundle(language, 'app', resources.default || resources);
}
} catch (error) {
console.error(`Failed to load language resources for ${language}:`, error);
}
};

export const changei18nLanguage = async (language: string) => {
await loadLanguage(language);
await i18n.changeLanguage(language);
};

let i18Init = i18n.use(languageFromConfig);

i18Init.init({
fallbackLng: 'en',
fallbackLng: defaultLang,

// have a common namespace used around the full app
ns: ['app', 'wallet'],
Expand All @@ -59,36 +81,19 @@ i18Init.init({
},

react: {
useSuspense : true, // Not using Suspense you will need to handle the not ready state yourself
useSuspense: true // Not using Suspense you will need to handle the not ready state yourself
},

backend: {
projectId: locizeProjectID,
referenceLng: 'en'
},
referenceLng: defaultLang
}
});

i18n.addResourceBundle('ar', 'app', appTranslationsAR);
i18n.addResourceBundle('cs', 'app', appTranslationsCS);
i18n.addResourceBundle('de', 'app', appTranslationsDE);
i18n.addResourceBundle('en', 'app', appTranslationsEN);
i18n.addResourceBundle('fr', 'app', appTranslationsFR);
i18n.addResourceBundle('ja', 'app', appTranslationsJA);
i18n.addResourceBundle('ms', 'app', appTranslationsMS);
i18n.addResourceBundle('nl', 'app', appTranslationsNL);
i18n.addResourceBundle('ru', 'app', appTranslationsRU);
i18n.addResourceBundle('pt', 'app', appTranslationsPT);
i18n.addResourceBundle('hi', 'app', appTranslationsHI);
i18n.addResourceBundle('bg', 'app', appTranslationsBG);
i18n.addResourceBundle('tr', 'app', appTranslationsTR);
i18n.addResourceBundle('zh', 'app', appTranslationsZH);
i18n.addResourceBundle('fa', 'app', appTranslationsFA);
i18n.addResourceBundle('es', 'app', appTranslationsES);
i18n.addResourceBundle('sl', 'app', appTranslationsSL);
i18n.addResourceBundle('he', 'app', appTranslationsHE);
i18n.addResourceBundle('it', 'app', appTranslationsIT);

i18n.on('languageChanged', (lng) => {
// load the default language first
loadLanguage(defaultLang);

i18n.on('languageChanged', async (lng) => {
// Set userLanguage in config back to empty if system locale matches
// the newly selected language lng to make the app use native-locale again.
// This also covers partial matches. For example, if native locale is pt_BR
Expand All @@ -97,18 +102,15 @@ i18n.on('languageChanged', (lng) => {
// Since userLanguage is stored in the backend config as a string,
// setting it to null here in JS turns it into an empty string "" in Go backend.
// This is ok since we're just checking for a truthy value in the language detector.
return getNativeLocale().then((nativeLocale) => {
let match = lng === nativeLocale;
if (!match) {
// There are too many combinations. So, we compare only the main
// language tag.
const lngLang = localeMainLanguage(lng);
const localeLang = localeMainLanguage(nativeLocale);
match = lngLang === localeLang;
}
const uiLang = match ? null : lng;
return setConfig({ backend: { userLanguage: uiLang } });
});
const nativeLocale = await getNativeLocale();
let match = lng === nativeLocale;
if (!match) {
const lngLang = localeMainLanguage(lng);
const localeLang = localeMainLanguage(nativeLocale);
match = lngLang === localeLang;
}
const uiLang = match ? null : lng;
return setConfig({ backend: { userLanguage: uiLang } });
});

export { i18n };
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { SingleDropdown } from '@/routes/settings/components/dropdowns/singledro
import { GlobeDark, GlobeLight } from '@/components/icon/icon';
import { useDarkmode } from '@/hooks/darkmode';
import styles from './languageDropDownSetting.module.css';
import { changei18nLanguage } from '@/i18n/i18n';

export const LanguageDropdownSetting = () => {
const { i18n, t } = useTranslation();
Expand All @@ -41,7 +42,7 @@ export const LanguageDropdownSetting = () => {
extraComponent={
<SingleDropdown
options={formattedLanguages}
handleChange={i18n.changeLanguage}
handleChange={changei18nLanguage}
value={{ label: selectedLanguage.display, value: selectedLanguage.code }}
/>
}
Expand Down

0 comments on commit 8ac388b

Please sign in to comment.