94 lines
2.6 KiB
JavaScript
94 lines
2.6 KiB
JavaScript
'use strict';
|
|
|
|
const I18N = (() => {
|
|
const SUPPORTED_LANGS = ['fr', 'en'];
|
|
const DEFAULT_LANG = 'fr';
|
|
const STORAGE_KEY = 'republican-calendar-lang';
|
|
|
|
let currentLang = DEFAULT_LANG;
|
|
let common = {};
|
|
let months = {};
|
|
let about = {};
|
|
const changeListeners = [];
|
|
|
|
function detectInitialLang() {
|
|
try {
|
|
const stored = localStorage.getItem(STORAGE_KEY);
|
|
if (stored && SUPPORTED_LANGS.includes(stored)) return stored;
|
|
} catch (error) {
|
|
// localStorage unavailable (private mode, etc.): fall through to default.
|
|
}
|
|
return DEFAULT_LANG;
|
|
}
|
|
|
|
function getByPath(object, path) {
|
|
return path.split('.').reduce((node, key) => (node && node[key] !== undefined ? node[key] : undefined), object);
|
|
}
|
|
|
|
function t(path, fallback) {
|
|
const value = getByPath(common, path);
|
|
return value !== undefined ? value : (fallback !== undefined ? fallback : path);
|
|
}
|
|
|
|
function applyStaticTranslations() {
|
|
document.documentElement.lang = currentLang;
|
|
document.querySelectorAll('[data-i18n]').forEach((element) => {
|
|
const key = element.getAttribute('data-i18n');
|
|
const value = getByPath(common, key);
|
|
if (value !== undefined) {
|
|
element.textContent = value;
|
|
}
|
|
});
|
|
}
|
|
|
|
async function fetchJson(url) {
|
|
const response = await fetch(url, { cache: 'no-store' });
|
|
if (!response.ok) {
|
|
throw new Error(`Failed to load ${url}: ${response.status}`);
|
|
}
|
|
return response.json();
|
|
}
|
|
|
|
async function load(lang) {
|
|
const targetLang = SUPPORTED_LANGS.includes(lang) ? lang : DEFAULT_LANG;
|
|
const [commonData, monthsData, aboutData] = await Promise.all([
|
|
fetchJson(`locales/${targetLang}/common.json`),
|
|
fetchJson(`locales/${targetLang}/months.json`),
|
|
fetchJson(`locales/${targetLang}/about.json`),
|
|
]);
|
|
currentLang = targetLang;
|
|
common = commonData;
|
|
months = monthsData;
|
|
about = aboutData;
|
|
applyStaticTranslations();
|
|
changeListeners.forEach((listener) => listener(currentLang));
|
|
}
|
|
|
|
async function setLang(lang) {
|
|
if (!SUPPORTED_LANGS.includes(lang) || lang === currentLang) return;
|
|
try {
|
|
localStorage.setItem(STORAGE_KEY, lang);
|
|
} catch (error) {
|
|
// Ignore storage failures; language just won't persist across reloads.
|
|
}
|
|
await load(lang);
|
|
}
|
|
|
|
function onChange(listener) {
|
|
changeListeners.push(listener);
|
|
}
|
|
|
|
return {
|
|
SUPPORTED_LANGS,
|
|
get currentLang() { return currentLang; },
|
|
get common() { return common; },
|
|
get months() { return months; },
|
|
get about() { return about; },
|
|
detectInitialLang,
|
|
load,
|
|
setLang,
|
|
onChange,
|
|
t,
|
|
};
|
|
})();
|