适配新语言
This commit is contained in:
@@ -11,6 +11,12 @@ import {
|
||||
} from "@/lib/luxsinApi";
|
||||
import { getDeviceIpFromUrl, resolveInitialDeviceIp } from "@/lib/deviceIp";
|
||||
import { isLikelyNetworkFetchError } from "@/lib/networkError";
|
||||
import {
|
||||
DEFAULT_DEVICE_LANGUAGE,
|
||||
getUiLanguageOverride,
|
||||
isDeviceLanguage,
|
||||
setUiLanguageOverride,
|
||||
} from "@/locales/resolveLocale";
|
||||
|
||||
interface DeviceContextType {
|
||||
ip: string;
|
||||
@@ -21,6 +27,16 @@ interface DeviceContextType {
|
||||
isDemoMode: boolean;
|
||||
setDemoMode: (demo: boolean) => void;
|
||||
deviceState: DeviceState | null;
|
||||
/**
|
||||
* 生效的界面显示语言索引(0-9)。前端语言覆盖(非设备语言 3-9)优先,
|
||||
* 否则跟随设备真实语言(0/1/2)。所有界面文案都应基于此值解析语言包。
|
||||
*/
|
||||
effectiveLanguage: number;
|
||||
/**
|
||||
* 设置界面语言:选择设备语言(0/1/2)会下发设备并清除前端覆盖;
|
||||
* 选择其他语言(3-9)仅做前端切换并持久化,绝不下发设备。
|
||||
*/
|
||||
setUiLanguage: (index: number) => Promise<void>;
|
||||
peqState: PeqState | null;
|
||||
lastUpdated: Date | null;
|
||||
error: string | null;
|
||||
@@ -55,6 +71,10 @@ export function DeviceProvider({ children }: { children: React.ReactNode }) {
|
||||
const [lastUpdated, setLastUpdated] = useState<Date | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [api, setApi] = useState<LuxsinAPI | null>(null);
|
||||
// 前端语言覆盖(仅非设备语言 3-9),初始值从 localStorage 恢复;null 表示跟随设备语言。
|
||||
const [uiLanguage, setUiLanguageState] = useState<number | null>(() => getUiLanguageOverride());
|
||||
// 记录上一次设备真实语言,用于检测设备侧语言变化(如轮询/刷新)后清除前端覆盖。
|
||||
const prevDeviceLanguageRef = useRef<number | undefined>(undefined);
|
||||
const lastMsgCountRef = useRef<number>(-1);
|
||||
const pollIntervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
||||
const autoConnectStartedRef = useRef(false);
|
||||
@@ -176,6 +196,37 @@ export function DeviceProvider({ children }: { children: React.ReactNode }) {
|
||||
setDeviceState(prev => prev ? { ...prev, ...params } : prev);
|
||||
}, [api, isDemoMode]);
|
||||
|
||||
// 生效的界面语言:前端覆盖优先,其次设备真实语言,最后默认。
|
||||
const effectiveLanguage = uiLanguage ?? deviceState?.language ?? DEFAULT_DEVICE_LANGUAGE;
|
||||
|
||||
const setUiLanguage = useCallback(async (index: number) => {
|
||||
if (isDeviceLanguage(index)) {
|
||||
// 设备支持的语言:清除前端覆盖并下发设备(沿用既有 setting 流程)。
|
||||
setUiLanguageOverride(null);
|
||||
setUiLanguageState(null);
|
||||
await updateSetting({ language: index });
|
||||
return;
|
||||
}
|
||||
// 非设备语言(3-9):仅前端切换 + 持久化,不下发设备。
|
||||
setUiLanguageOverride(index);
|
||||
setUiLanguageState(getUiLanguageOverride());
|
||||
}, [updateSetting]);
|
||||
|
||||
// 设备侧语言发生变化(用户在设备上切换语言,经 msgCount 轮询/刷新同步下来)时,
|
||||
// 清除前端语言覆盖,让 controller 跟随设备语言。
|
||||
// 说明:仅在设备语言「值发生变化」时触发;首次观测(prev 为 undefined)不清除,
|
||||
// 以保留跨会话持久化的前端覆盖(如上次选择了德语,刷新后仍应为德语)。
|
||||
useEffect(() => {
|
||||
const devLang = deviceState?.language;
|
||||
if (devLang === undefined) return;
|
||||
const prev = prevDeviceLanguageRef.current;
|
||||
prevDeviceLanguageRef.current = devLang;
|
||||
if (prev !== undefined && prev !== devLang) {
|
||||
setUiLanguageOverride(null);
|
||||
setUiLanguageState(null);
|
||||
}
|
||||
}, [deviceState?.language]);
|
||||
|
||||
const updatePeq = useCallback(async (filters: PeqFilter[]) => {
|
||||
if (isDemoMode) {
|
||||
setPeqState({ filters });
|
||||
@@ -277,6 +328,7 @@ export function DeviceProvider({ children }: { children: React.ReactNode }) {
|
||||
ip, setIp: setDeviceIp, ipMissing,
|
||||
isConnected, isConnecting, isDemoMode, setDemoMode,
|
||||
deviceState, peqState, lastUpdated, error,
|
||||
effectiveLanguage, setUiLanguage,
|
||||
connect, disconnect, refresh, api,
|
||||
updateSetting, updatePeq, upgradePeqChange, upgradePeqApply, syncPeqCatalog,
|
||||
setVolume, setInput, setOutput, setBalance,
|
||||
|
||||
Reference in New Issue
Block a user