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() на поле, чтобы прочитать его без реактивного отслеживания.

Установка начального языка

Начальный lang считывается из атрибута <html lang> во время createRoot, либо может быть задан явно:

tsx
import { createRoot } from 'preffx';

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

Сводка утилит

ОпцияТипОписание
defaultLangstringЯзык по умолчанию, когда ни один не задан/объявлен
setLang(value) => voidПереключение текущего языка
langReadonlySignalТекущий язык (реактивный)
dict(...) => DictProxyРеактивные поязыковые словари

Опубликовано под лицензией Apache License 2.0