feat: Building the app in it's first version
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
'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,
|
||||
};
|
||||
})();
|
||||
Reference in New Issue
Block a user