- lightweight, dependency-free i18n: typed fr/en dictionaries (fr is the source of truth, en must match its shape → compile error on drift), I18nProvider + useI18n hook, browser detection + localStorage persistence - FR/EN language switcher (home + room header) - translated the whole player-facing flow: home, join, pseudo, room shell, lobby (incl. settings, categories, bots, track submission, history), quiz, blindtest, player-cards, round transitions, end screen (podium, awards, tracks, rounds recap), and server-error messages (mapped by code) - history dates localized to the active language Content (questions/categories) intentionally left as-is. Back-office (admin) not translated yet — internal, token-gated tool. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
30 lines
654 B
TypeScript
30 lines
654 B
TypeScript
import { useState, type ReactNode } from "react"
|
|
import { fr } from "./fr"
|
|
import { en } from "./en"
|
|
import {
|
|
I18nContext,
|
|
LANG_KEY,
|
|
detectLang,
|
|
type Dict,
|
|
type Lang,
|
|
} from "./context"
|
|
|
|
export function I18nProvider({ children }: { children: ReactNode }) {
|
|
const [lang, setLangState] = useState<Lang>(detectLang)
|
|
const dicts: Record<Lang, Dict> = { fr, en }
|
|
|
|
function setLang(next: Lang) {
|
|
try {
|
|
localStorage.setItem(LANG_KEY, next)
|
|
} catch {
|
|
// ignore
|
|
}
|
|
setLangState(next)
|
|
}
|
|
|
|
return (
|
|
<I18nContext.Provider value={{ lang, t: dicts[lang], setLang }}>
|
|
{children}
|
|
</I18nContext.Provider>
|
|
)
|
|
}
|