简体中文 English 繁體中文 日本語 Русский
本文为静态镜像,内容以交互版为准 在交互式文档中心打开 →

Система международной локализации (i18n)

Начиная с ErisPulse v2.5.0, встроен полный функционал поддержки международной локализации. Ядро фреймворка и интерфейс CLI могут автоматически переключать отображаемый текст в зависимости от языка вашей системы, а также поддерживают регистрацию собственных переводов внешними модулями.

Поддерживаемые языки

Язык Код Описание
简体中文 zh-CN Язык по умолчанию (оригинальный язык фреймворка)
繁體中文 zh-TW Традиционный китайский (Гонконг/Макао/Тайвань)
English en Английский (общий язык по умолчанию)
日本語 ja Японский
Русский ru Русский

Быстрый опыт

Переключение через переменные среды

# Windows PowerShell
$env:ERISPULSE_LANG = "en"
epsdk run

# macOS / Linux
ERISPULSE_LANG=ja epsdk run

Переключение через конфигурационный файл

Добавьте в config/config.toml:

[ErisPulse.i18n]
language = "zh-TW"

Если установить значение "auto" (значение по умолчанию), язык будет определяться автоматически на основе языка системы.

Ручное переключение в коде

from ErisPulse import i18n

# Ручная установка языка
i18n.set_language("en")
print(i18n.get_language())  # "en"

# Возврат к автоматическому определению
i18n.reset_language()

Механизм определения языка

Фреймворк определяет язык пользователя в следующем порядке приоритетов:

  1. Переменная среды ERISPULSE_LANG — наивысший приоритет, используется для тестирования и временного переключения
  2. Windows API — GetUserDefaultLocaleName (только Windows, не подвержен влиянию переменных, таких как LANG, установленных в Git Bash и других инструментах)
  3. Переменные среды — LANGUAGE > LC_ALL > LC_MESSAGES > LANG (стандарт Unix/macOS)
  4. Системная локаль — locale.getlocale() / locale.getdefaultlocale()
  5. Резервное значение — en (английский язык)

Принцип ближайшего соответствия

При обнаружении языка, не соответствующего точно поддерживаемым, применяется принцип ближайшего соответствия:


Использование i18n в модулях

Вы можете зарегистрировать тексты перевода для своего модуля, чтобы ваш модуль также поддерживал множественные языки.

Рекомендуемый способ: объявление ключей перевода через I18nClass (v2.7.0+)

Начиная с версии v2.7.0, модули/адаптеры могут объявлять ключи перевода, подобно тому, как они объявляют ConfigClass, с помощью вложенного класса I18nClass. Фреймворк автоматически зарегистрирует все объявленные ключи перевода при загрузке, без необходимости вручную вызывать i18n.register().

from dataclasses import dataclass, field

from ErisPulse.Core.Bases import BaseConfig, BaseI18n, BaseModule, I18nKey


class MyModule(BaseModule):
    # Класс конфигурации (необязательно)
    @dataclass
    class ConfigClass(BaseConfig):
        welcome_msg: str = field(
            default="欢迎",
            metadata={
                # Здесь используется ключ перевода mymodule.welcome_msg
                "description": {"i18n": "mymodule.welcome_msg", "default": "Сообщение приветствия"},
            },
        )

    # Класс коллекции ключей перевода (необязательно)
    # Объявленные ключи будут автоматически зарегистрированы фреймворком, с приоритетом над генерацией значений по умолчанию ConfigClass
    class I18nClass(BaseI18n):
        # Имя атрибута автоматически объединяется в полный путь ключа: <имя_модуля>.<имя_атрибута>
        welcome_msg: I18nKey = I18nKey(
            default="Welcome Message",   # Языко-независимое значение по умолчанию, не регистрируется ни для какого языка
            zh_CN="欢迎消息",
            en="Welcome Message",
            ja="ウェルカムメッセージ",
            ru="Приветственное сообщение",
            zh_TW="歡迎訊息",
        )
        # Другие ключи перевода, используемые в бизнес-логике
        hello: I18nKey = I18nKey(
            default="Hello, {name}!",
            zh_CN="你好,{name}!",
            zh_TW="你好,{name}!",
            en="Hello, {name}!",
            ja="こんにちは、{name}!",
            ru="Привет, {name}!",
        )

        # Можно также явно указать полный путь ключа (не используя объединение по имени атрибута)
        custom: I18nKey = I18nKey(
            key="mymodule.deep.nested.key",
            default="Default text",
            zh_CN="默认文本",
            zh_TW="預設文本",
            en="Default text",
            ja="デフォルトテキスト",
            ru="Текст по умолчанию",
        )

Почему рекомендуется использовать I18nClass?

Сценарий Ручная регистрация i18n.register() Объявление через I18nClass
Ключи перевода, используемые в описании конфигурации Требуется ручная регистрация, и нужно успеть до генерации конфигурации Фреймворк автоматически регистрирует до генерации конфигурации
Объявление многоязычных переводов Распространены по разным on_load() Собраны в одном классе, легко читаемы
Согласованность именования ключей Легко допускать ошибки при написании Имя атрибута используется как суффикс ключа, IDE может подсказывать
Очистка при выгрузке Требуется ручной unregister_domain() Фреймворк использует единый domain для регистрации

Правила формирования пути ключей в I18nClass

Использование в адаптере

Адаптеры также поддерживают I18nClass, и способ использования полностью идентичен:

from ErisPulse import BaseAdapter
from ErisPulse.Core.Bases import BaseConfig, BaseI18n, I18nKey


class MyAdapter(BaseAdapter):
    @dataclass
    class ConfigClass(BaseConfig):
        endpoint: str = field(
            default="",
            metadata={
                # Описание конфигурации ссылается на ключ adapter.MyAdapter.endpoint
                "description": {"i18n": "MyAdapter.endpoint", "default": "Адрес API"},
            },
        )

    class I18nClass(BaseI18n):
        # Собранное объявление ключей перевода, используемых в описании конфигурации и других бизнес-ключей
        endpoint: I18nKey = I18nKey(
            default="API Endpoint",
            zh_CN="API 地址",
            zh_TW="API 位址",
            en="API Endpoint",
            ja="APIアドレス",
            ru="API адрес",
        )

I18nClass адаптера будет автоматически зарегистрирован на этапе __init__ (до генерации шаблона конфигурации), обеспечивая доступность ключей перевода, используемых в описании конфигурации.

Ручная регистрация пользовательских переводов (старый способ)

Если вы не используете I18nClass, вы можете напрямую вызвать i18n.register() для регистрации текстов перевода.

from ErisPulse import i18n

# Регистрация китайского перевода
i18n.register("zh-CN", {
    "my_module.welcome": "欢迎使用我的模块!",
    "my_module.goodbye": "再见!",
    "my_module.hello": "你好,{name}!",
}, domain="my_module")

# Регистрация английского перевода
i18n.register("en", {
    "my_module.welcome": "Welcome to my module!",
    "my_module.goodbye": "Goodbye!",
    "my_module.hello": "Hello, {name}!",
}, domain="my_module")

Использование перевода

from ErisPulse import i18n

# Простой перевод
i18n.t("my_module.welcome")  # Автоматически используется текущий язык

# С параметрами форматирования
i18n.t("my_module.hello", name="Alice")

# Указание значения по умолчанию (возвращается, если ключ перевода не найден)
i18n.t("my_module.unknown_key", default="默认文本")

Использование в классе модуля

from dataclasses import dataclass, field
from ErisPulse import i18n
from ErisPulse.Core.Bases import BaseConfig, BaseModule

@dataclass
class MyModuleConfig(BaseConfig):
    welcome_msg: str = field(
        default="欢迎",
        metadata={
            "description": {"i18n": "my_module.welcome_msg", "default": "Сообщение приветствия"},
            "ui": {"widget": "text", "group": "basic", "order": 1},
        },
    )

class MyModule(BaseModule):
    ConfigClass = MyModuleConfig

    async def on_load(self, event):
        # Динамическое чтение конфигурации (каждый раз отражает последнее значение)
        self.logger.info(self.cfg.welcome_msg)
        self.logger.info(i18n.t("my_module.welcome"))

    @command("hello")
    async def hello_handler(self, event):
        name = event.get_user_nickname() or "friend"
        await event.reply(i18n.t("my_module.hello", name=name))

    async def on_unload(self, event):
        pass

Удаление перевода

# Удаление перевода всего домена
i18n.unregister_domain("my_module")

Многоязычные поля конфигурации

Начиная с версии v2.5.2, схема конфигурации полностью поддерживает i18n. Все видимые пользователю текстовые поля могут ссылаться на ключи i18n, и WebUI и другие потребители автоматически будут преобразовывать их в соответствующий текст в зависимости от текущего языка.

Поддерживаемые поля i18n

Поле Расположение Описание
description метаданные поля Описание поля
options[].label ui.options Метки опций для контрола select
placeholder ui.placeholder Заполнитель для поля ввода
group_labels _schema_meta Названия групп (заголовки разделов Dashboard)

Используется единый формат {"i18n": "key", "default": "текст"}, а строки без i18n будут передаваться без изменений (для обратной совместимости).

Объявление полей i18n

Все видимые пользователю текстовые поля поддерживают i18n:

from dataclasses import dataclass, field
from ErisPulse.Core.Bases import BaseConfig

@dataclass
class MyAdapterConfig(BaseConfig):
    # i18n для description
    token: str = field(
        default="",
        metadata={
            "description": {"i18n": "my_adapter.token", "default": "Токен платформы"},
            "required": True,
            "secret": True,
            "ui": {
                "widget": "password",
                "group": "basic",
                "order": 1,
                # i18n для placeholder
                "placeholder": {"i18n": "my_adapter.token.ph", "default": "Введите токен"},
            },
        },
    )
    # i18n для label опций
    mode: str = field(
        default="a",
        metadata={
            "description": {"i18n": "my_adapter.mode", "default": "Режим работы"},
            "ui": {
                "widget": "select",
                "group": "basic",
                "order": 2,
                "options": [
                    {"label": {"i18n": "my_adapter.mode.a", "default": "Режим A"}, "value": "a"},
                    {"label": {"i18n": "my_adapter.mode.b", "default": "Режим B"}, "value": "b"},
                ],
            },
        },
    )

    # i18n для group_labels (название группы)
    _schema_meta = {
        "group_labels": {
            "basic": {"i18n": "my_adapter.group.basic", "default": "Основные настройки"},
        }
    }

default — это резервный текст, который будет отображаться, если перевод не зарегистрирован или поиск не удался.

Секретная обработка и проверка конфигурации

Поля, помеченные как "secret": True, автоматически получают защиту от раскрытия (начиная с версии 2.7.0):

from ErisPulse.Core.Bases.config_schema import redact_secret

redact_secret("sk-xxxxxx")  # '***'
redact_secret("")           # ''

Проверка конфигурации (validate_config()) помимо проверки обязательных полей на непустоту, начиная с версии 2.7.0, поддерживает:

Проверка Метаданные Пример
Соответствие типа Объявление типа поля Если для поля типа int передано строковое значение, будет ошибка
Ограничение по перечислению ui.options или options на уровне верхнего уровня Значение должно принадлежать разрешённым опциям
Диапазон значений min / max на уровне верхнего уровня metadata={"min": 1, "max": 65535}
from ErisPulse.Core.Bases.config_schema import validate_config

@dataclass
class C(BaseConfig):
    mode: str = field(default="a", metadata={"ui": {"widget": "select", "options": ["a", "b"]}})
    port: int = field(default=80, metadata={"min": 1, "max": 65535})

errors = validate_config(C(mode="x", port=70000))  # Две ошибки: перечисление + диапазон

Регистрация переводов для конфигурации

Ключи i18n для полей конфигурации регистрируются так же, как и обычные ключи перевода, с помощью i18n.register():

from ErisPulse import i18n

# Регистрация китайского (согласуется с default, но может отличаться)
i18n.register("zh-CN", {
    "my_adapter.token": "Токен платформы",
}, domain="my_adapter")

# Регистрация английского
i18n.register("en", {
    "my_adapter.token": "Platform Token",
}, domain="my_adapter")

Рекомендуемый способ: использование I18nClass для объявления ключей перевода, фреймворк автоматически зарегистрирует их (см. раздел «Рекомендуемый способ» выше), без необходимости вызывать i18n.register() или register_config_i18n().

Также доступна удобная функция register_config_i18n(), которая автоматически извлекает ключи из класса конфигурации и регистрирует их:

from ErisPulse.Core.Bases.config_schema import register_config_i18n

# Автоматически извлекает description.default как перевод для zh-CN
register_config_i18n(MyAdapterConfig, "zh-CN")

# Ручная передача английского перевода
register_config_i18n(MyAdapterConfig, "en", {
    "my_adapter.token": "Platform Token",
})

Как WebUI потребляет i18n

Функция get_config_schema() возвращает схему, в которой словарь i18n будет передан без изменений. Frontend WebUI может использовать i18n.t() для получения перевода в зависимости от текущего языка.

Если требуется серверная сторона для преобразования i18n в строки (например, для передачи на фронтенд, который не поддерживает i18n), используется resolve_config_schema(), который преобразует description, options[].label, placeholder и group_labels в текст текущего языка:

from ErisPulse.Core.Bases.config_schema import resolve_config_schema

# Все поля i18n преобразованы в текст текущего языка
schema = resolve_config_schema(MyAdapterConfig)
print(schema["fields"]["token"]["description"])    # "Токен платформы" или "Platform Token"
print(schema["fields"]["token"]["placeholder"])   # "Введите токен" или "Enter Token"
print(schema["fields"]["mode"]["options"][0]["label"])  # "Режим A" или "Mode A"
print(schema["group_labels"]["basic"])             # "Основные настройки" или "Basic"

Типы и инструменты BaseConfig, BotAccountConfig, register_config_i18n(), resolve_config_schema() и другие определены в ErisPulse.Core.Bases.config_schema. ErisPulse.runtime.config_schema сохранён для обратной совместимости, рекомендуется импортировать из ErisPulse.Core.Bases (за исключением типов, связанных с ключами i18n, которые находятся в ErisPulse.Core.Bases.i18n_schema).

API-справочник

I18nManager

Основные методы

Метод Описание
t(key, default=None, **kwargs) Получение переведённого текста (gettext() — это псевдоним)
set_language(lang) Ручная установка языка
get_language() Получение текущего языка
reset_language() Сброс на автоматическое определение (и повторное определение среды)
get_supported_languages() Получение списка всех поддерживаемых языков
has_translation(key, lang=None) Проверка существования ключа перевода
register(lang, translations, domain) Регистрация пользовательских переводов
unregister_domain(domain) Удаление всех переводов указанной области
reload() Перезагрузка встроенных переводов и повторное определение языка

Подробности метода t()

def t(self, key, /, default=None, **kwargs):

Пример:

# Определение перевода: "greeting": "你好,{name}!欢迎来到{place}。"
i18n.t("greeting", name="Alice", place="ErisPulse")
# Возвращает: "你好,Alice!欢迎来到ErisPulse。"

BaseI18n / I18nKey (объявления ключей перевода)

Начиная с v2.7.0, ErisPulse.Core.Bases предоставляет инструмент объявления ключей перевода на основе атрибутов класса (рекомендуется импортировать из ErisPulse.Core.Bases):

I18nKey.default — это текст по умолчанию, независимый от языка, не регистрируется ни в один язык. Для того чтобы перевод заработал, необходимо явно передать хотя бы один параметр языка (zh_CN= / en= / ja= и т.д.). Таким образом, разработчики из разных стран могут свободно использовать родной язык для заполнения default, фреймворк не делает никаких предположений.

Название Описание
I18nKey(default, *, key=None, zh_CN, zh_TW, en, ja, ru) Объявление одного ключа перевода, default — текст по умолчанию, независимый от языка
BaseI18n Базовый класс для объявления набора ключей перевода (имена соответствуют BaseConfig), подклассы объявляют несколько I18nKey в атрибутах класса
BaseI18n.register(prefix="", domain="app") Классовый метод: регистрация всех объявленных ключей в систему перевода
key Псевдоним I18nKey (более лаконичная запись)

Пример использования:

from ErisPulse.Core.Bases import BaseI18n, key

class MyKeys(BaseI18n):
    # Упрощённая запись с псевдонимом
    hello = key(
        default="Hello",
        zh_CN="你好",
        zh_TW="你好",
        en="Hello",
        ja="こんにちは",
        ru="Привет",
    )
    bye = key(
        default="Bye",
        zh_CN="再见",
        zh_TW="再見",
        en="Bye",
        ja="さようなら",
        ru="До свидания",
    )

# Независимое использование (ручная регистрация)
MyKeys.register(prefix="myapp.", domain="myapp")

Доступ к i18n через экземпляр SDK

from ErisPulse import sdk

# sdk.i18n и импортированный i18n — это один и тот же объект
sdk.i18n.set_language("en")
print(sdk.i18n.t("core.sdk.init.starting"))

Конфигурация во время выполнения

Чтение конфигурации i18n через API-конфигурации

from ErisPulse.Core.Bases import I18nConfig
from ErisPulse.runtime import get_i18n_config

config = get_i18n_config()
print(config["language"])  # "auto" или код конкретного языка

# I18nConfig — это dataclass, который можно использовать для генерации шаблона конфигурации
schema = I18nConfig.__dataclass_fields__

Описание параметров конфигурации

В разделе [ErisPulse.i18n] файла config/config.toml:

[ErisPulse.i18n]
# Язык отображения, возможные значения:
# - "auto"      — автоматическое определение языка системы (по умолчанию)
# - "zh-CN"     — упрощённый китайский
# - "zh-TW"     — традиционный китайский
# - "en"        — английский
# - "ja"        — японский
# - "ru"        — русский
language = "auto"

Лучшие практики

Именование ключей перевода

Рекомендуется использовать формат именования с разделением точками:

<имя_модуля>.<категория>.<описание>

Например: my_module.command.hello_desc, core.adapter.start_failed

Многоязычная поддержка

Не обязательно предоставлять переводы сразу для всех языков. Отсутствующие языки будут автоматически возвращаться к английскому. Если английский также отсутствует, будет отображаться сам ключ.

Динамические данные

Для динамически генерируемых данных (например, имен пользователей, количества и т.д.) используйте формат {placeholder}:

# Определение перевода
"user_count": "Текущее количество пользователей онлайн: {count} человек"

# Использование
i18n.t("user_count", count=len(users))

Сообщения в логах

Если ваш модуль использует логгер фреймворка, эти сообщения также будут автоматически отображаться на текущем языке:

self.logger.info(i18n.t("my_module.startup"))

Отношение к i18n CLI

CLI имеет независимый модуль международной локализации (ErisPulse.CLI.i18n), который полностью отделен от основного модуля международной локализации фреймворка.

Такая архитектура гарантирует, что изменения в переводах CLI не повлияют на стабильность основного ядра фреймворка.