forked from pdxlocations/meshconfig
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathi18n.js
More file actions
104 lines (89 loc) · 2.9 KB
/
i18n.js
File metadata and controls
104 lines (89 loc) · 2.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
// i18n.js - lightweight vanilla i18n with auto-detection
const i18n = (() => {
const translations = {};
let currentLang = "en";
let fallbackLang = "en";
let path = "/i18n"; // folder where JSON files live
const supportedLangs = ["en", "pt", "es"]; // adjust as needed
// Normalize language code
function normalizeLang(lang) {
if (!lang) return fallbackLang;
const short = lang.toLowerCase().split("-")[0];
return supportedLangs.includes(short) ? short : fallbackLang;
}
// Detect language (URL -> localStorage -> browser -> fallback)
function detectLanguage() {
const params = new URLSearchParams(window.location.search);
const urlLang = params.get("lang");
if (urlLang) {
localStorage.setItem("lang", urlLang);
return normalizeLang(urlLang);
}
const saved = localStorage.getItem("lang");
if (saved) return normalizeLang(saved);
const browser = navigator.language || navigator.userLanguage;
return normalizeLang(browser);
}
// Load language JSON
async function loadLanguage(lang) {
const normalized = normalizeLang(lang);
if (!translations[normalized]) {
try {
const res = await fetch(`${path}/${normalized}.json`);
if (!res.ok) throw new Error(`Missing ${normalized}.json`);
translations[normalized] = await res.json();
} catch (e) {
console.warn(`i18n: failed to load ${normalized}, falling back`);
if (normalized !== fallbackLang) return loadLanguage(fallbackLang);
return;
}
}
currentLang = normalized;
localStorage.setItem("lang", currentLang);
updateDOM();
}
// Get nested translation
function getNested(obj, key) {
return key.split('.').reduce((o, i) => o?.[i], obj);
}
// Translate function
function t(key, vars = {}) {
let str =
getNested(translations[currentLang], key) ??
getNested(translations[fallbackLang], key) ??
key;
// Replace {{var}}
Object.keys(vars).forEach(k => {
str = str.replace(new RegExp(`{{\\s*${k}\\s*}}`, "g"), vars[k]);
});
return str;
}
// Update DOM
function updateDOM() {
document.querySelectorAll("[data-i18n]").forEach(el => {
const key = el.getAttribute("data-i18n");
el.innerText = t(key);
});
document.querySelectorAll("[data-i18n-attr]").forEach(el => {
const attrMap = el.getAttribute("data-i18n-attr").split(";");
attrMap.forEach(pair => {
const [attr, key] = pair.split(":");
if (attr && key) el.setAttribute(attr.trim(), t(key.trim()));
});
});
}
// Init system
async function init(options = {}) {
if (options.path) path = options.path;
if (options.fallback) fallbackLang = options.fallback;
const lang = options.lang || detectLanguage();
await loadLanguage(lang);
}
return {
init,
t,
setLang: loadLanguage,
getLang: () => currentLang,
};
})();
window.i18n = i18n;