anvilsign in

collin/mahjong

master / src / ui / LanguageToggle.tsx
1import { LANGS, type Lang, type Prefs } from '../game/prefs';
2import { useT } from './lang';
3
4interface Props {
5 prefs: Prefs;
6 onPrefs: (p: Prefs) => void;
7 /** Icon only, for the cramped row of controls in the middle of the table. */
8 compact?: boolean;
9}
10
11/** Glyph, and what the button says it is currently showing. Each language is
12 * named in itself, the way a language picker should be; only 'both' is a word
13 * in whatever is being read. */
14const MODES: Record<Lang, { icon: string; label: [string, string] }> = {
15 bi: { icon: '雙', label: ['雙語', 'Bilingual'] },
16 zh: { icon: '中', label: ['繁體中文', '繁體中文'] },
17 en: { icon: 'A', label: ['English', 'English'] },
18};
19
20/**
21 * Bilingual · 繁體中文 · English. Three states rather than two, so it steps
22 * round on each tap instead of flipping — and names the mode it is in now,
23 * not the one the tap would take you to.
24 *
25 * It sits on the lobby beside 設定 rather than only inside it: the player who
26 * needs it most is the one who cannot read the button that opens Settings.
27 */
28export function LanguageToggle({ prefs, onPrefs, compact }: Props) {
29 const t = useT();
30 const mode = MODES[prefs.lang];
31 const label = t(...mode.label);
32 const next = LANGS[(LANGS.indexOf(prefs.lang) + 1) % LANGS.length];
33 return (
34 <button
35 className={`btn ghost pill lang-toggle ${compact ? 'compact' : ''}`}
36 aria-label={`${t('語言', 'Language')}: ${label}`}
37 onClick={() => onPrefs({ ...prefs, lang: next })}
38 >
39 <span className="lang-icon" aria-hidden>
40 {mode.icon}
41 </span>
42 {!compact && <span>{label}</span>}
43 </button>
44 );
45}