Skip to content

国际化(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() 可在不进行响应式跟踪的情况下读取它。

设置初始语言

初次 langcreateRoot 时从 <html lang> 属性读取,也可以显式设置:

tsx
import { createRoot } from 'preffx';

createRoot({ defaultLang: 'en' }).mount(App);

工具函数汇总

选项类型描述
defaultLangstring未设置/声明语言时的回退语言
setLang(value) => void切换当前语言
langReadonlySignal当前语言(响应式)
dict(...) => DictProxy响应式、按语言划分的字典

根据 Apache-2.0 许可证发布。