nerdware/apps/web/src/i18n/provider.tsx
AyoubBenziza 1e5ef2be90 feat(web): i18n (FR/EN) — homemade typed dictionary
- 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>
2026-06-11 19:03:28 +02:00

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>
)
}