export const APP_LOCALE_COOKIE = 'nanobot_locale'; export const APP_LOCALE_STORAGE_KEY = 'nanobot_locale'; export const APP_LOCALES = ['zh-CN', 'en-US'] as const; export type AppLocale = (typeof APP_LOCALES)[number]; export function isAppLocale(value: string | null | undefined): value is AppLocale { return value === 'zh-CN' || value === 'en-US'; } export function normalizeAppLocale(value?: string | null): AppLocale { const probe = value?.trim().toLowerCase() || ''; if (probe.startsWith('en')) { return 'en-US'; } return 'zh-CN'; } function readCookieLocale(): string | null { if (typeof document === 'undefined') { return null; } const match = document.cookie .split('; ') .find((item) => item.startsWith(`${APP_LOCALE_COOKIE}=`)); if (!match) { return null; } return decodeURIComponent(match.slice(APP_LOCALE_COOKIE.length + 1)); } export function readBrowserAppLocale(): AppLocale { if (typeof window === 'undefined') { return 'zh-CN'; } const fromDocument = document.documentElement.lang; if (fromDocument) { return normalizeAppLocale(fromDocument); } const fromStorage = window.localStorage.getItem(APP_LOCALE_STORAGE_KEY); if (fromStorage) { return normalizeAppLocale(fromStorage); } const fromCookie = readCookieLocale(); if (fromCookie) { return normalizeAppLocale(fromCookie); } return normalizeAppLocale(window.navigator.language); } export function persistAppLocale(locale: AppLocale): void { if (typeof window === 'undefined') { return; } document.documentElement.lang = locale; window.localStorage.setItem(APP_LOCALE_STORAGE_KEY, locale); document.cookie = `${APP_LOCALE_COOKIE}=${encodeURIComponent(locale)}; path=/; max-age=31536000; samesite=lax`; } export function getCurrentAppLocale(): AppLocale { if (typeof window === 'undefined') { return 'zh-CN'; } return readBrowserAppLocale(); } export function pickAppText(locale: AppLocale, zhValue: T, enValue: T): T { return locale === 'en-US' ? enValue : zhValue; }