Files
dusuniot/src/admin-i18n/index.tsx
T

73 lines
2.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* # 模块 后台 i18n 核心(React 上下文 + t 函数)
* 词典以中文源串为 key(各页面直接 t("中文字符串")),en 词典提供英文译文,
* 缺词回退源串(中文)。默认英文:localStorage 未设置时 locale=en。
*/
import { createContext, useContext, useEffect, useState, type ReactNode } from "react";
import {
getAdminLocalePref,
setAdminLocalePref,
type AdminDict,
type AdminLocaleDef,
} from "./shared";
import { en } from "./locales/en";
// 客户端安全部分原样转出
export { ADMIN_LOCALES, ADMIN_LOCALE_STORAGE_KEY, ADMIN_THEME_STORAGE_KEY, getAdminLocalePref, setAdminLocalePref, getAdminThemePref, setAdminThemePref } from "./shared";
export type { AdminDict, AdminLocaleDef, AdminTheme } from "./shared";
/** # 成员变量 词典注册表(locale → AdminDict;en 启动即注册) **/
const dicts = new Map<string, AdminDict>([["en", en]]);
/** # 函数 注册某语言的后台词典 */
export function registerAdminDict(code: string, dict: AdminDict): void {
dicts.set(code, dict);
}
/** # 函数 构造后台翻译函数:按词典查 key,缺词回退源串(中文),支持 {var} 插值 */
export function createAdminT(locale: string) {
const d = dicts.get(locale);
return (key: string, vars?: Record<string, string | number>): string => {
let s: string = d?.[key] ?? key;
if (vars) for (const [k, v] of Object.entries(vars)) s = s.replaceAll(`{${k}}`, String(v));
return s;
};
}
/** # 类型 后台 t 函数 **/
export type AdminT = ReturnType<typeof createAdminT>;
// ---- React 上下文 ----
/** # 成员变量 后台语言上下文(默认英文) **/
const AdminLocaleContext = createContext<{ locale: string; setLocale: (code: string) => void; t: AdminT }>({
locale: "en",
setLocale: () => {},
t: createAdminT("en"),
});
/** # 组件 后台语言提供者(纯客户端:SSR 渲染英文,水合后读 localStorage 切换,无水合不一致) */
export function AdminLocaleProvider(props: { children: ReactNode }) {
const [locale, setLocaleState] = useState("en");
// 水合后应用本地偏好(effect 中 setState 不触发水合校验)
useEffect(() => {
setLocaleState(getAdminLocalePref());
}, []);
/** # 函数 切换后台语言:写 localStorage → 重渲染(组件树即时切换,无需整页刷新) */
const setLocale = (code: string) => {
setAdminLocalePref(code);
setLocaleState(code);
};
const t = createAdminT(locale);
return <AdminLocaleContext.Provider value={{ locale, setLocale, t }}>{props.children}</AdminLocaleContext.Provider>;
}
/** # 函数 组件内取后台语言上下文(locale / setLocale / t) */
export function useAdminI18n() {
return useContext(AdminLocaleContext);
}
/** # 函数 组件内取后台翻译函数(便捷别名) */
export function useAdminT(): AdminT {
return useContext(AdminLocaleContext).t;
}