返回 CodeWhale
locale-switcher.tsx
根目录 / web / components / locale-switcher.tsx
1 "use client";
2
3 import { useRouter, usePathname } from "next/navigation";
4 import { ALL_LOCALES } from "@/lib/i18n/config";
5 import { fill, getChrome } from "@/lib/i18n/dictionaries";
6 import { replacePathLocale } from "@/lib/i18n/path";
7
8 /** Labels for the dropdown. Keyed by locale code, displayed in native script. */
9 const LOCALE_LABELS: Record<string, string> = {};
10 for (const l of ALL_LOCALES) {
11 LOCALE_LABELS[l.code] = l.label;
12 }
13
14 /** Routed locales that appear in the switcher (shipped + partial). */
15 const ROUTED = ALL_LOCALES.filter((l) => l.status === "shipped" || l.status === "partial");
16
17 export function LocaleSwitcher({ current }: { current: string }) {
18 const router = useRouter();
19 const pathname = usePathname();
20 const chrome = getChrome(current);
21
22 const switchLocale = (code: string) => {
23 if (code === current) return;
24 document.cookie = `NEXT_LOCALE=${code};path=/;max-age=${60 * 60 * 24 * 365}`;
25 router.push(replacePathLocale(pathname, code));
26 };
27
28 // If only 1 routed locale, no switcher needed.
29 if (ROUTED.length <= 1) return null;
30
31 // If exactly 2 routed locales, show a simple toggle.
32 if (ROUTED.length === 2) {
33 const other = ROUTED.find((l) => l.code !== current);
34 if (!other) return null;
35 return (
36 <button
37 onClick={() => switchLocale(other.code)}
38 className="font-mono text-[0.72rem] uppercase text-ink-mute hover:text-indigo transition-colors px-2 py-1"
39 aria-label={fill(chrome.switcherSwitchTo, { label: other.label })}
40 >
41 {other.label}
42 </button>
43 );
44 }
45
46 // 3+ routed locales: show a dropdown. Partial packs carry a visible
47 // badge so the incomplete scope is honest at the point of selection.
48 return (
49 <select
50 value={current}
51 onChange={(e) => switchLocale(e.target.value)}
52 className="font-mono text-[0.72rem] uppercase text-ink-mute bg-transparent hairline-t hairline-b hairline-l hairline-r px-2 py-1 cursor-pointer hover:text-indigo transition-colors"
53 aria-label={chrome.switcherLabel}
54 >
55 {ROUTED.map((l) => (
56 <option key={l.code} value={l.code}>
57 {l.status === "partial" ? `${l.label} ${chrome.partialBadge}` : l.label}
58 </option>
59 ))}
60 </select>
61 );
62 }
63
63 lines Plain Text