国际化(i18n)
PreffX 使用单一的根级语言信号来驱动所有翻译。语言存储在 <html lang="..."> 属性中,并与 lang 信号同步,因此更改它会自动更新所有字典。
读取语言
lang 工具函数是一个只读信号,保存当前的语言代码:
tsx
import type { PC } from 'preffx';
export const CurrentLang: PC = (_props, { lang }) => {
return <span>Current language: {lang}</span>;
};在 computed 块内部,该值会被响应式地跟踪:
tsx
import type { PC } from 'preffx';
export const Caption: PC = (_props, { computed, lang }) => {
const greeting = computed(() =>
lang.value === 'ru' ? 'Привет' : 'Hello'
);
return <p>{greeting}</p>;
};切换语言
调用 setLang 来更改语言。它会写入 <html lang> 属性,并进而更新所有字典使用的 lang 信号。
tsx
import type { PC } from 'preffx';
export const LangSwitch: PC = (_props, { setLang }) => {
return (
<div>
<button onClick={() => setLang('en')}>EN</button>
<button onClick={() => setLang('ru')}>RU</button>
</div>
);
};简单字典
传入一个以语言为键的普通对象,然后通过 computed 选择当前激活的对象:
tsx
import type { PC } from 'preffx';
const dictionary = {
en: { openProfile: 'Open profile', showStats: 'Show statistics' },
ru: { openProfile: 'Открыть профиль', showStats: 'Показать статистику' }
};
export const App: PC = (_props, { computed, lang }) => {
const captions = computed(() => dictionary[lang.value] || dictionary.en);
return (
<div>
<button>{captions.openProfile}</button>
<button>{captions.showStats}</button>
</div>
);
};使用 dict 的懒加载字典
当某个语言区域很大或异步加载时,请使用 dict 工具函数。它接受一个「语言代码 → 解析器」(同步或异步)的映射,并返回一个字段为响应式信号的代理对象。将字段作为值访问,或作为函数调用。
tsx
import type { APC } from 'preffx';
const getRuDictionary = async () => {
return {
openProfile(name: string) {
return 'Открыть профиль № ' + name;
},
showStats: 'Показать статистику'
};
};
const getEnDictionary = async () => {
return {
openProfile(name: string) {
return 'Open profile № ' + name;
},
showStats: 'Show stats'
};
};
export const App: APC = async (_props, { dict, setLang }) => {
const initialDict = await getEnDictionary();
const captions = dict({ ru: getRuDictionary, en: getEnDictionary }, initialDict);
return (
<div>
<button onClick={() => setLang('en')}>EN</button>
<button onClick={() => setLang('ru')}>RU</button>
<span>
<button>{captions.openProfile('42')}</button>
<button>{captions.showStats}</button>
</span>
</div>
);
};dict(resolvers)— 第一个参数:Record<lang, () => Dict | Promise<Dict>>。dict(resolvers, initial)— 第二个可选参数提供在解析器完成之前使用的初始对象。- 当语言改变时,当前
lang的解析器会自动重新运行。 - 函数字段会变成可调用的,并返回计算信号:
captions.openProfile('42')是响应式的。 - 在字段上使用
.peek()可在不进行响应式跟踪的情况下读取它。
设置初始语言
初次 lang 在 createRoot 时从 <html lang> 属性读取,也可以显式设置:
tsx
import { createRoot } from 'preffx';
createRoot({ defaultLang: 'en' }).mount(App);工具函数汇总
| 选项 | 类型 | 描述 |
|---|---|---|
defaultLang | string | 未设置/声明语言时的回退语言 |
setLang | (value) => void | 切换当前语言 |
lang | ReadonlySignal | 当前语言(响应式) |
dict | (...) => DictProxy | 响应式、按语言划分的字典 |