improve load times by consolidating metadata into languages.json and only loading the requested language
This commit is contained in:
parent
1397764251
commit
3fb38f8614
|
|
@ -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`
|
// `languages.json`
|
||||||
export type LanguagesJson = {
|
export type LanguagesJson = {
|
||||||
// Array of language IDs to allow loading
|
// 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)
|
// The default language (set if a invalid language not in the languages array is set, or no language is set)
|
||||||
defaultLanguage: string;
|
defaultLanguage: string;
|
||||||
|
|
@ -148,7 +153,7 @@ interface StringKeyMap {
|
||||||
/// our fancy internationalization helper.
|
/// our fancy internationalization helper.
|
||||||
export class I18n {
|
export class I18n {
|
||||||
// The language data itself
|
// 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 lang: Language = fallbackLanguage;
|
||||||
private countryNames: {[key: string]: string} | null = null;
|
private countryNames: {[key: string]: string} | null = null;
|
||||||
private languageDropdown: HTMLSpanElement = document.getElementById('languageDropdown') as HTMLSpanElement;
|
private languageDropdown: HTMLSpanElement = document.getElementById('languageDropdown') as HTMLSpanElement;
|
||||||
|
|
@ -164,20 +169,13 @@ export class I18n {
|
||||||
var res = await fetch("lang/languages.json");
|
var res = await fetch("lang/languages.json");
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
alert("Failed to load languages.json: " + res.statusText);
|
alert("Failed to load languages.json: " + res.statusText);
|
||||||
await this.SetLanguage(fallbackLanguage, fallbackId);
|
await this.SetLanguage(fallbackId);
|
||||||
this.ReplaceStaticStrings();
|
this.ReplaceStaticStrings();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var langData = await res.json() as LanguagesJson;
|
var langData = await res.json() as LanguagesJson;
|
||||||
for (const langId of langData.languages) {
|
for (const langId in langData.languages) {
|
||||||
let path = `./lang/${langId}.json`;
|
this.langs.set(langId, langData.languages[langId]);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
this.langs.forEach((_lang, langId) => {
|
this.langs.forEach((_lang, langId) => {
|
||||||
// Add to language dropdown
|
// Add to language dropdown
|
||||||
|
|
@ -187,7 +185,7 @@ export class I18n {
|
||||||
a.innerText = `${_lang.flag} ${_lang.languageName}`;
|
a.innerText = `${_lang.flag} ${_lang.languageName}`;
|
||||||
a.addEventListener('click', async e => {
|
a.addEventListener('click', async e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
await this.SetLanguage(_lang, langId);
|
await this.SetLanguage(langId);
|
||||||
this.ReplaceStaticStrings();
|
this.ReplaceStaticStrings();
|
||||||
});
|
});
|
||||||
this.languageDropdown.appendChild(a);
|
this.languageDropdown.appendChild(a);
|
||||||
|
|
@ -201,7 +199,7 @@ export class I18n {
|
||||||
else if (this.langs.has(browserLang)) lang = browserLang;
|
else if (this.langs.has(browserLang)) lang = browserLang;
|
||||||
else {
|
else {
|
||||||
// If the exact browser language isn't in the list, try to find a language with the same prefix
|
// 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]) {
|
if (langId.split('-')[0] === browserLang.split('-')[0]) {
|
||||||
lang = langId;
|
lang = langId;
|
||||||
break;
|
break;
|
||||||
|
|
@ -210,7 +208,7 @@ export class I18n {
|
||||||
}
|
}
|
||||||
// If all else fails, use the default language
|
// If all else fails, use the default language
|
||||||
if (lang === null) lang = langData.defaultLanguage;
|
if (lang === null) lang = langData.defaultLanguage;
|
||||||
await this.SetLanguage(this.langs.get(lang) as Language, lang);
|
await this.SetLanguage(lang);
|
||||||
this.ReplaceStaticStrings();
|
this.ReplaceStaticStrings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -230,9 +228,23 @@ export class I18n {
|
||||||
return this.countryNames[code] || code;
|
return this.countryNames[code] || code;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async SetLanguage(lang: Language, id: string) {
|
private async SetLanguage(id: string) {
|
||||||
let lastId = this.langId;
|
let lastId = this.langId;
|
||||||
this.langId = id;
|
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.lang = lang;
|
||||||
|
|
||||||
this.countryNames = await this.getCountryNames(id);
|
this.countryNames = await this.getCountryNames(id);
|
||||||
|
|
|
||||||
|
|
@ -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"
|
"defaultLanguage": "en-us"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user