Localization: Fallback to English when missing translation and while loading

This commit is contained in:
Alexander Zinchuk 2021-06-30 00:23:11 +03:00
parent 4e558131eb
commit 92b79ce62a
3 changed files with 1736 additions and 14 deletions

View File

@ -14,7 +14,7 @@ addReducer('init', (global) => {
const { animationLevel, messageTextSize, language } = global.settings.byKey; const { animationLevel, messageTextSize, language } = global.settings.byKey;
const theme = selectTheme(global); const theme = selectTheme(global);
setLanguage(language); setLanguage(language, undefined, true);
document.documentElement.style.setProperty('--message-text-size', `${messageTextSize}px`); document.documentElement.style.setProperty('--message-text-size', `${messageTextSize}px`);
document.body.classList.add('initial'); document.body.classList.add('initial');

1704
src/util/fallbackLangPack.ts Normal file

File diff suppressed because it is too large Load Diff

View File

@ -13,6 +13,7 @@ interface LangFn {
isRtl?: boolean; isRtl?: boolean;
} }
const FALLBACK_LANG_CODE = 'en';
const PLURAL_OPTIONS = ['value', 'zeroValue', 'oneValue', 'twoValue', 'fewValue', 'manyValue', 'otherValue'] as const; const PLURAL_OPTIONS = ['value', 'zeroValue', 'oneValue', 'twoValue', 'fewValue', 'manyValue', 'otherValue'] as const;
const PLURAL_RULES = { const PLURAL_RULES = {
/* eslint-disable max-len */ /* eslint-disable max-len */
@ -39,7 +40,8 @@ const PLURAL_RULES = {
const cache = new Map<string, string>(); const cache = new Map<string, string>();
let langPack: ApiLangPack; let langPack: ApiLangPack | undefined;
let fallbackLangPack: ApiLangPack | undefined;
const { const {
addCallback, addCallback,
@ -59,12 +61,16 @@ export const getTranslation: LangFn = (key: string, value?: any, format?: 'i') =
} }
} }
if (!langPack) { if (!langPack && !fallbackLangPack) {
return key; return key;
} }
const langString = langPack[key]; const langString = (langPack && langPack[key]) || (fallbackLangPack && fallbackLangPack[key]);
if (!langString) { if (!langString) {
if (!fallbackLangPack) {
void importFallbackLangPack();
}
return key; return key;
} }
@ -85,7 +91,7 @@ export const getTranslation: LangFn = (key: string, value?: any, format?: 'i') =
return template; return template;
}; };
export async function setLanguage(langCode: string, callback?: NoneToVoidFunction) { export async function setLanguage(langCode: string, callback?: NoneToVoidFunction, withFallback = false) {
if (langPack && langCode === currentLangCode) { if (langPack && langCode === currentLangCode) {
if (callback) { if (callback) {
callback(); callback();
@ -94,9 +100,16 @@ export async function setLanguage(langCode: string, callback?: NoneToVoidFunctio
return; return;
} }
const newLangPack = await fetchFromCacheOrRemote(langCode); let newLangPack = await cacheApi.fetch(LANG_CACHE_NAME, langCode, cacheApi.Type.Json);
if (!newLangPack) { if (!newLangPack) {
return; if (withFallback) {
await importFallbackLangPack();
}
newLangPack = await fetchRemote(langCode);
if (!newLangPack) {
return;
}
} }
cache.clear(); cache.clear();
@ -113,15 +126,19 @@ export async function setLanguage(langCode: string, callback?: NoneToVoidFunctio
callback(); callback();
} }
runCallbacks(langPack); runCallbacks();
} }
async function fetchFromCacheOrRemote(langCode: string): Promise<ApiLangPack | undefined> { async function importFallbackLangPack() {
const cached = await cacheApi.fetch(LANG_CACHE_NAME, langCode, cacheApi.Type.Json); if (fallbackLangPack) {
if (cached) { return;
return cached;
} }
fallbackLangPack = (await import('./fallbackLangPack')).default;
runCallbacks();
}
async function fetchRemote(langCode: string): Promise<ApiLangPack | undefined> {
const remote = await callApi('fetchLangPack', { sourceLangPacks: LANG_PACKS, langCode }); const remote = await callApi('fetchLangPack', { sourceLangPacks: LANG_PACKS, langCode });
if (remote) { if (remote) {
await cacheApi.save(LANG_CACHE_NAME, langCode, remote.langPack); await cacheApi.save(LANG_CACHE_NAME, langCode, remote.langPack);
@ -132,8 +149,9 @@ async function fetchFromCacheOrRemote(langCode: string): Promise<ApiLangPack | u
} }
function getPluralOption(amount: number) { function getPluralOption(amount: number) {
const optionIndex = currentLangCode && PLURAL_RULES[currentLangCode as keyof typeof PLURAL_RULES] const langCode = currentLangCode || FALLBACK_LANG_CODE;
? PLURAL_RULES[currentLangCode as keyof typeof PLURAL_RULES](amount) const optionIndex = PLURAL_RULES[langCode as keyof typeof PLURAL_RULES]
? PLURAL_RULES[langCode as keyof typeof PLURAL_RULES](amount)
: 0; : 0;
return PLURAL_OPTIONS[optionIndex]; return PLURAL_OPTIONS[optionIndex];