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) — 2 番目の任意引数は、リゾルバーが解決される前に使用される初期オブジェクトを提供します。
  • 現在の 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 License 2.0に基づいて公開されています。