61 lines
1.6 KiB
JavaScript
61 lines
1.6 KiB
JavaScript
const SUPPORTED = ['fr', 'en'];
|
|
const FALLBACK = 'fr';
|
|
const STORAGE_KEY = 'cv-lang';
|
|
|
|
let translations = {};
|
|
let currentLang = FALLBACK;
|
|
|
|
function detectLang() {
|
|
const stored = localStorage.getItem(STORAGE_KEY);
|
|
if (stored && SUPPORTED.includes(stored)) return stored;
|
|
const browser = (navigator.language || '').slice(0, 2).toLowerCase();
|
|
return SUPPORTED.includes(browser) ? browser : FALLBACK;
|
|
}
|
|
|
|
async function loadTranslations(lang) {
|
|
const res = await fetch(`js/locales/${lang}.json`);
|
|
return res.json();
|
|
}
|
|
|
|
// Resolve a dot-notation key like "form.nom"
|
|
function resolve(key) {
|
|
return key.split('.').reduce((obj, k) => obj?.[k], translations) ?? key;
|
|
}
|
|
|
|
// Replace {{var}} placeholders
|
|
export function t(key, vars = {}) {
|
|
let str = resolve(key);
|
|
for (const [k, v] of Object.entries(vars)) {
|
|
str = str.replaceAll(`{{${k}}}`, v);
|
|
}
|
|
return str;
|
|
}
|
|
|
|
export function getLang() {
|
|
return currentLang;
|
|
}
|
|
|
|
// Apply translations to all elements with [data-i18n] and [data-i18n-placeholder]
|
|
function applyTranslations() {
|
|
document.querySelectorAll('[data-i18n]').forEach(el => {
|
|
el.textContent = t(el.dataset.i18n);
|
|
});
|
|
document.querySelectorAll('[data-i18n-placeholder]').forEach(el => {
|
|
el.placeholder = t(el.dataset.i18nPlaceholder);
|
|
});
|
|
document.documentElement.lang = currentLang;
|
|
}
|
|
|
|
export async function changeLanguage(lang) {
|
|
if (!SUPPORTED.includes(lang)) return;
|
|
translations = await loadTranslations(lang);
|
|
currentLang = lang;
|
|
localStorage.setItem(STORAGE_KEY, lang);
|
|
applyTranslations();
|
|
}
|
|
|
|
export async function initI18n() {
|
|
const lang = detectLang();
|
|
await changeLanguage(lang);
|
|
}
|