improve load times by consolidating metadata into languages.json and only loading the requested language

This commit is contained in:
Elijah R 2024-06-23 03:51:42 -04:00
parent 1397764251
commit 3fb38f8614
2 changed files with 102 additions and 17 deletions

View File

@ -126,10 +126,15 @@ export type Language = {
};
};
export type LanguageMetadata = {
languageName: string;
flag: string; // country flag, can be blank if not applicable. will be displayed in language dropdown
};
// `languages.json`
export type LanguagesJson = {
// Array of language IDs to allow loading
languages: Array<string>;
languages: {[key: string]: LanguageMetadata};
// The default language (set if a invalid language not in the languages array is set, or no language is set)
defaultLanguage: string;
@ -148,7 +153,7 @@ interface StringKeyMap {
/// our fancy internationalization helper.
export class I18n {
// The language data itself
private langs : Map<string, Language> = new Map<string, Language>();
private langs : Map<string, LanguageMetadata> = new Map<string, Language>();
private lang: Language = fallbackLanguage;
private countryNames: {[key: string]: string} | null = null;
private languageDropdown: HTMLSpanElement = document.getElementById('languageDropdown') as HTMLSpanElement;
@ -164,20 +169,13 @@ export class I18n {
var res = await fetch("lang/languages.json");
if (!res.ok) {
alert("Failed to load languages.json: " + res.statusText);
await this.SetLanguage(fallbackLanguage, fallbackId);
await this.SetLanguage(fallbackId);
this.ReplaceStaticStrings();
return;
}
var langData = await res.json() as LanguagesJson;
for (const langId of langData.languages) {
let path = `./lang/${langId}.json`;
let res = await fetch(path);
if (!res.ok) {
console.error(`Failed to load lang/${langId}.json: ${res.statusText}`);
continue;
}
let _lang = await res.json() as Language;
this.langs.set(langId, _lang);
for (const langId in langData.languages) {
this.langs.set(langId, langData.languages[langId]);
}
this.langs.forEach((_lang, langId) => {
// Add to language dropdown
@ -187,7 +185,7 @@ export class I18n {
a.innerText = `${_lang.flag} ${_lang.languageName}`;
a.addEventListener('click', async e => {
e.preventDefault();
await this.SetLanguage(_lang, langId);
await this.SetLanguage(langId);
this.ReplaceStaticStrings();
});
this.languageDropdown.appendChild(a);
@ -201,7 +199,7 @@ export class I18n {
else if (this.langs.has(browserLang)) lang = browserLang;
else {
// If the exact browser language isn't in the list, try to find a language with the same prefix
for (let langId of langData.languages) {
for (let langId in langData.languages) {
if (langId.split('-')[0] === browserLang.split('-')[0]) {
lang = langId;
break;
@ -210,7 +208,7 @@ export class I18n {
}
// If all else fails, use the default language
if (lang === null) lang = langData.defaultLanguage;
await this.SetLanguage(this.langs.get(lang) as Language, lang);
await this.SetLanguage(lang);
this.ReplaceStaticStrings();
}
@ -230,9 +228,23 @@ export class I18n {
return this.countryNames[code] || code;
}
private async SetLanguage(lang: Language, id: string) {
private async SetLanguage(id: string) {
let lastId = this.langId;
this.langId = id;
let lang;
if (id === fallbackId) lang = fallbackLanguage;
else {
let path = `./lang/${id}.json`;
let res = await fetch(path);
if (!res.ok) {
console.error(`Failed to load lang/${id}.json: ${res.statusText}`);
await this.SetLanguage(fallbackId);
return;
}
lang = await res.json() as Language;
}
this.lang = lang;
this.countryNames = await this.getCountryNames(id);

View File

@ -1,4 +1,77 @@
{
"languages": ["en-us", "fr-fr", "de-de", "ja-jp", "pl-pl", "es-es", "ru-ru", "hu-hu", "uk-ua", "hr-hr", "ro-ro", "tok", "id", "ko-kr", "pt-br", "th", "zh-cn", "vi-vn"],
"languages": {
"en-us": {
"languageName": "English (US)",
"flag": "🇺🇸"
},
"fr-fr": {
"languageName": "Français",
"flag": "🇫🇷"
},
"de-de": {
"languageName": "Deutsch",
"flag": "🇩🇪"
},
"ja-jp": {
"languageName": "日本語",
"flag": "🇯🇵"
},
"pl-pl": {
"languageName": "Polski",
"flag": "🇵🇱"
},
"es-es": {
"languageName": "Español (España)",
"flag": "🇪🇸"
},
"ru-ru": {
"languageName": "Русский",
"flag": "🇷🇺"
},
"hu-hu": {
"languageName": "Magyar",
"flag": "🇭🇺"
},
"uk-ua": {
"languageName": "Українська",
"flag": "🇺🇦"
},
"hr-hr": {
"languageName": "Hrvatski",
"flag": "🇭🇷"
},
"ro-ro": {
"languageName": "Română",
"flag": "🇷🇴"
},
"tok": {
"languageName": "Toki Pona",
"flag": "🌐"
},
"id": {
"languageName": "Bahasa Indonesia",
"flag": "🇮🇩"
},
"ko-kr": {
"languageName": "한국어",
"flag": "🇰🇷"
},
"pt-br": {
"languageName": "Português (Brasil)",
"flag": "🇧🇷"
},
"th": {
"languageName": "ภาษาไทย",
"flag": "🇹🇭"
},
"zh-cn": {
"languageName": "中文 (简体)",
"flag": "🇨🇳"
},
"vi-vn": {
"languageName": "Tiếng Việt",
"flag": "🇻🇳"
}
},
"defaultLanguage": "en-us"
}