// Interface language: fr, en, es (Yvv, 2026-10-21). French is the source. import { en } from './en'; import { es } from './es'; import { fr, type Key, type Messages } from './fr'; import type { Lang } from '../types'; export const LANGS: Lang[] = ['fr', 'en', 'es']; const CATALOGS: Record = { fr, en, es }; const LOCALES: Record = { fr: 'fr-FR', en: 'en-GB', es: 'es-ES' }; function initial(): Lang { try { const saved = localStorage.getItem('ld:lang'); if (saved === 'fr' || saved === 'en' || saved === 'es') return saved; } catch { /* no storage */ } const nav = (typeof navigator !== 'undefined' ? navigator.language : 'fr').slice(0, 2); return nav === 'en' || nav === 'es' ? nav : 'fr'; } class I18n { lang = $state('fr'); init() { this.set(initial()); } set(lang: Lang) { this.lang = lang; document.documentElement.lang = lang; try { localStorage.setItem('ld:lang', lang); } catch { /* no storage */ } } t = (key: Key, params: Record = {}): string => { const raw = CATALOGS[this.lang][key] ?? fr[key] ?? key; return raw.replace(/\{(\w+)\}/g, (_, k: string) => (k in params ? String(params[k]) : `{${k}}`)); }; /** `.one` / `.other` — French says « 0 session », English and Spanish « 0 sessions ». */ plural = (key: string, n: number, params: Record = {}): string => { const one = this.lang === 'fr' ? n <= 1 : n === 1; return this.t(`${key}.${one ? 'one' : 'other'}` as Key, { n, ...params }); }; date = (iso: string | null | undefined): string => { if (!iso) return ''; return new Date(iso).toLocaleDateString(LOCALES[this.lang], { day: 'numeric', month: 'long', year: 'numeric' }); }; number = (n: number): string => n.toLocaleString(LOCALES[this.lang]); } export const i18n = new I18n(); export const t = i18n.t; export type { Key };