适配新语言

This commit is contained in:
eafonyang
2026-10-22 13:50:27 +08:00
parent e89dca8c2f
commit ff62849d18
18 changed files with 4492 additions and 73 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+106 -6
View File
@@ -1,19 +1,119 @@
import localeEn from "./data-en.json";
import localeZh from "./data-zh.json";
import localeZhHK from "./data-zh-HK.json";
import localeDe from "./data-de.json";
import localeEs from "./data-es.json";
import localeFr from "./data-fr.json";
import localeKo from "./data-ko.json";
import localeVi from "./data-vi.json";
import localePt from "./data-pt.json";
import localePl from "./data-pl.json";
/** Device language index: 0=en, 1=zh-HK, 2=zh-CN */
/**
* 语言索引映射:
* 0=en, 1=zh-HK, 2=zh-CN → 设备固件支持,选择后会下发 dev/info.cgi?action=setting&language=
* 3=de, 4=es, 5=fr, 6=ko, 7=vi, 8=pt, 9=pl → 设备不支持,仅在 controller 前端做界面语言切换,绝不下发设备
*/
export const DEFAULT_DEVICE_LANGUAGE = 0;
/** 设备固件真正支持的语言索引,只有这些允许下发到设备。 */
export const DEVICE_LANGUAGE_INDICES = [0, 1, 2] as const;
export type LocalePack = typeof localeEn;
const LOCALE_PACKS = [
localeEn,
localeZhHK,
localeZh,
localeDe,
localeEs,
localeFr,
localeKo,
localeVi,
localePt,
localePl,
] as LocalePack[];
/** 语言选择器可展示的全部语言数量(含仅前端语言)。 */
export const LANGUAGE_COUNT = LOCALE_PACKS.length;
/**
* 语言选择器固定显示的「母语名称」,与当前界面语言无关。
* 数组下标即语言索引(0=en … 9=pl),选择时直接作为语言值使用。
*/
export const LANGUAGE_NATIVE_LABELS = [
"English",
"繁體中文",
"简体中文",
"Deutsch",
"Español",
"Français",
"한국어",
"Tiếng Việt",
"Português (Portugal)",
"Polski",
];
/** 是否为设备支持的语言(0/1/2),只有这些才会下发设备。 */
export function isDeviceLanguage(index: number | undefined | null): boolean {
return index === 0 || index === 1 || index === 2;
}
/** 是否为已知的语言索引(0..LANGUAGE_COUNT-1)。 */
export function isKnownLanguage(index: number | undefined | null): boolean {
return typeof index === "number" && index >= 0 && index < LOCALE_PACKS.length;
}
/** 设备真实语言(不含前端覆盖),仅用于需要区分设备语言的场景。 */
export function resolveDeviceLanguage(language: number | undefined): number {
return language ?? DEFAULT_DEVICE_LANGUAGE;
}
export function resolveLocalePack(language: number | undefined): LocalePack {
const lang = resolveDeviceLanguage(language);
if (lang === 0) return localeEn;
if (lang === 1) return localeZhHK;
return localeZh;
/* ── 前端语言覆盖(仅非设备语言 3..9,持久化到 localStorage,绝不下发设备) ── */
const UI_LANGUAGE_OVERRIDE_KEY = "luxsin.uiLanguageOverride";
function readStoredOverride(): number | null {
try {
const raw = localStorage.getItem(UI_LANGUAGE_OVERRIDE_KEY);
if (raw == null) return null;
const n = Number(raw);
return isKnownLanguage(n) && !isDeviceLanguage(n) ? n : null;
} catch {
return null;
}
}
let uiLanguageOverride: number | null = readStoredOverride();
/** 读取当前前端语言覆盖;无覆盖(跟随设备语言)时返回 null。 */
export function getUiLanguageOverride(): number | null {
return uiLanguageOverride;
}
/**
* 设置前端语言覆盖。传入仅前端语言(3..9)时生效并持久化;
* 传入设备语言(0/1/2)或 null 时清除覆盖,恢复跟随设备语言。
*/
export function setUiLanguageOverride(index: number | null): number | null {
uiLanguageOverride =
isKnownLanguage(index) && !isDeviceLanguage(index) ? (index as number) : null;
try {
if (uiLanguageOverride == null) localStorage.removeItem(UI_LANGUAGE_OVERRIDE_KEY);
else localStorage.setItem(UI_LANGUAGE_OVERRIDE_KEY, String(uiLanguageOverride));
} catch {
/* localStorage 不可用时忽略,仅保留内存态 */
}
return uiLanguageOverride;
}
/** 生效的显示语言:前端覆盖优先,其次设备语言,最后默认。 */
export function resolveEffectiveLanguage(language: number | undefined): number {
if (uiLanguageOverride != null) return uiLanguageOverride;
const l = language ?? DEFAULT_DEVICE_LANGUAGE;
return isKnownLanguage(l) ? l : DEFAULT_DEVICE_LANGUAGE;
}
export function resolveLocalePack(language: number | undefined): LocalePack {
const l = language ?? DEFAULT_DEVICE_LANGUAGE;
return LOCALE_PACKS[isKnownLanguage(l) ? l : DEFAULT_DEVICE_LANGUAGE] ?? localeEn;
}