返回 JoyAI-Echo
config.ts
1 export const LOCALE_STORAGE_KEY = "nanobot.locale";
2
3 export const supportedLocales = [
4 { code: "en", label: "English", nativeLabel: "English" },
5 { code: "zh-CN", label: "Chinese (Simplified)", nativeLabel: "简体中文" },
6 { code: "zh-TW", label: "Chinese (Traditional)", nativeLabel: "繁體中文" },
7 { code: "fr", label: "French", nativeLabel: "Français" },
8 { code: "ja", label: "Japanese", nativeLabel: "日本語" },
9 { code: "ko", label: "Korean", nativeLabel: "한국어" },
10 { code: "es", label: "Spanish", nativeLabel: "Español" },
11 { code: "vi", label: "Vietnamese", nativeLabel: "Tiếng Việt" },
12 { code: "id", label: "Indonesian", nativeLabel: "Bahasa Indonesia" },
13 ] as const;
14
15 export type SupportedLocale = (typeof supportedLocales)[number]["code"];
16
17 export const defaultLocale: SupportedLocale = "en";
18 export const fallbackLocale: SupportedLocale = "en";
19
20 export function normalizeLocale(
21 input: string | null | undefined,
22 ): SupportedLocale {
23 if (!input) return defaultLocale;
24 const trimmed = input.trim();
25 if (!trimmed) return defaultLocale;
26
27 const exact = supportedLocales.find((locale) => locale.code === trimmed);
28 if (exact) return exact.code;
29
30 const lower = trimmed.toLowerCase();
31 if (lower === "zh" || lower.startsWith("zh-cn") || lower.startsWith("zh-sg")) {
32 return "zh-CN";
33 }
34 if (
35 lower.startsWith("zh-tw") ||
36 lower.startsWith("zh-hk") ||
37 lower.startsWith("zh-mo") ||
38 lower.startsWith("zh-hant")
39 ) {
40 return "zh-TW";
41 }
42
43 const base = lower.split("-")[0];
44 const baseMatch = supportedLocales.find(
45 (locale) => locale.code.toLowerCase() === base,
46 );
47 return baseMatch?.code ?? defaultLocale;
48 }
49
50 export function readStoredLocale(): SupportedLocale | null {
51 if (typeof window === "undefined") return null;
52 try {
53 const raw = window.localStorage.getItem(LOCALE_STORAGE_KEY);
54 return raw ? normalizeLocale(raw) : null;
55 } catch {
56 return null;
57 }
58 }
59
60 export function detectNavigatorLocale(): SupportedLocale {
61 if (typeof navigator === "undefined") return defaultLocale;
62 const candidates = [
63 ...(navigator.languages ?? []),
64 navigator.language,
65 ].filter(Boolean);
66 for (const locale of candidates) {
67 const normalized = normalizeLocale(locale);
68 if (normalized) return normalized;
69 }
70 return defaultLocale;
71 }
72
73 export function resolveInitialLocale(): SupportedLocale {
74 return defaultLocale;
75 }
76
77 export function persistLocale(locale: SupportedLocale): void {
78 if (typeof window === "undefined") return;
79 try {
80 window.localStorage.setItem(LOCALE_STORAGE_KEY, locale);
81 } catch {
82 // ignore storage errors
83 }
84 }
85
86 export function applyDocumentLocale(locale: SupportedLocale): void {
87 if (typeof document === "undefined") return;
88 document.documentElement.lang = locale;
89 }
90
91 export function localeOption(locale: SupportedLocale) {
92 return supportedLocales.find((entry) => entry.code === locale) ?? supportedLocales[0];
93 }
94
94 lines TYPESCRIPT