nerdware/apps/web/src/components/pseudo-screen.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

47 lines
1.6 KiB
TypeScript

import { useState } from "react"
import { Button } from "@workspace/ui/components/button"
import { useRoomStore } from "@/store/room"
import { Avatar } from "@/components/avatar"
import { useI18n } from "@/i18n/context"
const inputClass =
"border-input bg-background h-10 w-full rounded-md border px-3 py-2 text-center text-sm focus-visible:ring-ring focus-visible:ring-2 focus-visible:outline-none"
/** Choix du pseudo dans la room, avec aperçu de l'avatar en temps réel. */
export function PseudoScreen({ code }: { code: string }) {
const setName = useRoomStore((s) => s.setName)
const { t } = useI18n()
const [name, setName_] = useState("")
const canSubmit = name.trim().length > 0
return (
<div className="flex min-h-svh items-center justify-center p-6">
<div className="flex w-full max-w-xs flex-col items-center gap-5">
<p className="text-muted-foreground text-xs uppercase">
{t.pseudo.room(code)}
</p>
<Avatar
seed={name.trim() || "?"}
className="size-28 ring-2 ring-primary/30"
/>
<p className="font-heading text-lg font-bold">{t.pseudo.choose}</p>
<input
className={inputClass}
placeholder={t.pseudo.placeholder}
value={name}
maxLength={24}
autoFocus
onChange={(e) => setName_(e.target.value)}
onKeyDown={(e) => e.key === "Enter" && canSubmit && setName(name)}
/>
<Button
className="w-full"
disabled={!canSubmit}
onClick={() => setName(name)}
>
{t.pseudo.enter}
</Button>
</div>
</div>
)
}