60 lines
1.8 KiB
JavaScript
60 lines
1.8 KiB
JavaScript
// Lightweight i18n loader. Translation data lives in `i18n/<lang>.json`, one
|
|
// file per language so translators can edit each file independently.
|
|
|
|
const I18N_AVAILABLE = ["en", "de", "pl", "ru", "fr", "es", "pt"];
|
|
const I18N_NAMES = {
|
|
en: "English", de: "Deutsch", pl: "Polski", ru: "Русский",
|
|
fr: "Français", es: "Español", pt: "Português"
|
|
};
|
|
const I18N_BASE_PATH = "i18n";
|
|
|
|
const I18N_DATA = {};
|
|
const I18N_PENDING = {};
|
|
let CURRENT_LANG = "en";
|
|
|
|
function i18nURL(lang) {
|
|
return `${I18N_BASE_PATH}/${lang}.json`;
|
|
}
|
|
|
|
async function loadLang(lang) {
|
|
if (!I18N_AVAILABLE.includes(lang)) lang = "en";
|
|
if (I18N_DATA[lang]) return I18N_DATA[lang];
|
|
if (I18N_PENDING[lang]) return I18N_PENDING[lang];
|
|
const p = fetch(i18nURL(lang), { cache: "no-cache" })
|
|
.then((r) => r.ok ? r.json() : Promise.reject(new Error("i18n_load_failed")))
|
|
.then((data) => { I18N_DATA[lang] = data; delete I18N_PENDING[lang]; return data; })
|
|
.catch((err) => { delete I18N_PENDING[lang]; throw err; });
|
|
I18N_PENDING[lang] = p;
|
|
return p;
|
|
}
|
|
|
|
async function setLang(lang) {
|
|
if (!I18N_AVAILABLE.includes(lang)) lang = "en";
|
|
// English is the fallback for missing keys, so make sure it is always loaded.
|
|
if (!I18N_DATA.en) {
|
|
try { await loadLang("en"); } catch (e) {}
|
|
}
|
|
if (lang !== "en") {
|
|
try { await loadLang(lang); } catch (e) {}
|
|
}
|
|
CURRENT_LANG = lang;
|
|
document.documentElement.lang = lang;
|
|
}
|
|
|
|
function t(key, vars) {
|
|
const d = I18N_DATA[CURRENT_LANG] || I18N_DATA.en || {};
|
|
const fallback = I18N_DATA.en || {};
|
|
let str = d[key] || fallback[key] || key;
|
|
if (vars) {
|
|
for (const k in vars) {
|
|
str = str.replaceAll("{" + k + "}", vars[k]);
|
|
}
|
|
}
|
|
return str;
|
|
}
|
|
|
|
function detectInitialLang() {
|
|
const navLang = (navigator.language || "en").slice(0, 2);
|
|
return I18N_AVAILABLE.includes(navLang) ? navLang : "en";
|
|
}
|