55 lines
1.7 KiB
TypeScript
55 lines
1.7 KiB
TypeScript
|
|
import fr from './fr.json'
|
||
|
|
import gcf from './gcf.json'
|
||
|
|
import { browser } from '$app/environment'
|
||
|
|
import { defaultLocale, type Locale } from './locales'
|
||
|
|
|
||
|
|
export { locales, localeNames, defaultLocale, type Locale } from './locales'
|
||
|
|
|
||
|
|
type Dict = typeof fr
|
||
|
|
// gcf est vérifié par TypeScript contre la forme du français : une clé
|
||
|
|
// manquante est une erreur de build, pas un fallback silencieux (ADR-001).
|
||
|
|
const dicts: Record<Locale, Dict> = { fr, gcf }
|
||
|
|
|
||
|
|
const STORAGE_KEY = 'oki-lang'
|
||
|
|
|
||
|
|
function initialLocale(): Locale {
|
||
|
|
if (!browser) return defaultLocale
|
||
|
|
const saved = localStorage.getItem(STORAGE_KEY)
|
||
|
|
return saved === 'gcf' || saved === 'fr' ? saved : defaultLocale
|
||
|
|
}
|
||
|
|
|
||
|
|
// Rune d'état partagée (Svelte 5) : la langue de l'interface.
|
||
|
|
// Le HTML prérendu est en fr ; le créole est une amélioration progressive
|
||
|
|
// (persistance localStorage — mission §4.3, doctrine §4.2).
|
||
|
|
const state = $state({ locale: initialLocale() })
|
||
|
|
|
||
|
|
export function getLocale(): Locale {
|
||
|
|
return state.locale
|
||
|
|
}
|
||
|
|
|
||
|
|
export function setLocale(locale: Locale) {
|
||
|
|
state.locale = locale
|
||
|
|
if (browser) {
|
||
|
|
localStorage.setItem(STORAGE_KEY, locale)
|
||
|
|
document.documentElement.lang = locale
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function lookup(dict: Dict, path: string): unknown {
|
||
|
|
return path.split('.').reduce<unknown>((node, key) => {
|
||
|
|
if (node && typeof node === 'object' && key in node) {
|
||
|
|
return (node as Record<string, unknown>)[key]
|
||
|
|
}
|
||
|
|
return undefined
|
||
|
|
}, dict)
|
||
|
|
}
|
||
|
|
|
||
|
|
/** Chaîne d'interface pour `path` dans la langue courante (réactif). */
|
||
|
|
export function t(path: string): string {
|
||
|
|
const hit = lookup(dicts[state.locale], path) ?? lookup(dicts[defaultLocale], path)
|
||
|
|
if (typeof hit !== 'string') {
|
||
|
|
throw new Error(`Clé i18n manquante « ${path} » (locale : ${state.locale})`)
|
||
|
|
}
|
||
|
|
return hit
|
||
|
|
}
|