Files
cv-generator/js/i18n.js
T
2026-04-05 21:54:22 +01:00

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);
}