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