简体中文 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,不受 Git Bash 等工具覆盖 LANG 的影响)
  3. 环境变量 — LANGUAGE > LC_ALL > LC_MESSAGES > LANG(Unix/macOS 标准)
  4. 系统 Locale — 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={
                # 这里引用了 i18n 键 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 声明式
配置描述引用的 i18n 键 需手动注册,且要赶在配置生成前 框架自动在配置生成前注册
多语言翻译声明 散落在各个 on_load() 中 集中在类里,一目了然
键名命名一致性 容易拼写错误 属性名作为键名后缀,IDE 可补全
卸载时清理 需手动 unregister_domain() 框架使用统一 domain 注册

I18nClass 的键路径规则

在适配器中使用

适配器同样支持 I18nClass,使用方式完全一致:

from ErisPulse.Core 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__ 阶段(即配置模板生成之前)自动注册,确保配置描述引用的 i18n 键已可用。

手动注册自定义翻译(旧写法)

如果不使用 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 起,配置 Schema 全面支持 i18n。所有用户可见的文本字段均可引用 i18n 键,WebUI 和其他消费者会自动根据当前语言解析为对应文本。

支持的 i18n 字段

字段 位置 说明
description field metadata 字段描述
options[].label ui.options select 控件选项标签
placeholder ui.placeholder 输入框占位符
group_labels _schema_meta 分组显示名(Dashboard 分区标题)

统一采用 {"i18n": "key", "default": "文本"} 格式,纯字符串则原样透传(向后兼容)。

声明 i18n 字段

所有用户可见文本字段都支持 i18n:

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

@dataclass
class MyAdapterConfig(BaseConfig):
    # description i18n
    token: str = field(
        default="",
        metadata={
            "description": {"i18n": "my_adapter.token", "default": "平台 Token"},
            "required": True,
            "secret": True,
            "ui": {
                "widget": "password",
                "group": "basic",
                "order": 1,
                # placeholder i18n
                "placeholder": {"i18n": "my_adapter.token.ph", "default": "请输入 Token"},
            },
        },
    )
    # options label i18n
    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"},
                ],
            },
        },
    )

    # group_labels i18n(分组显示名)
    _schema_meta = {
        "group_labels": {
            "basic": {"i18n": "my_adapter.group.basic", "default": "基本设置"},
        }
    }

default 是兜底文本——当翻译未注册或查找失败时显示。

secret 脱敏与配置校验

标记为 "secret": True 的字段会自动获得脱敏保护(2.7.0 起):

from ErisPulse.Core.Bases.config_schema import redact_secret

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

配置校验(validate_config())除 required 非空检查外,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": "平台 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 如何消费

get_config_schema() 返回的 schema 中,i18n 字典会原样透传。WebUI 前端可以根据当前语言调用 i18n.t() 解析。

如果需要服务端直接解析为字符串(如返回给不支持 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"])    # "平台 Token" 或 "Platform Token"
print(schema["fields"]["token"]["placeholder"])   # "请输入 Token" 或 "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 保留为兼容性 shim, 推荐从 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") 类方法:注册所有声明的键到 i18n系统
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")

从 SDK 实例访问

from ErisPulse import sdk

# sdk.i18n 与直接导入的 i18n 是同一个对象
sdk.i18n.set_language("en")
print(sdk.i18n.t("core.sdk.init.starting"))

运行时配置

通过配置 API 读取 i18n 配置

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__

配置项说明

在 config/config.toml 的 [ErisPulse.i18n] 部分:

[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))

日志消息

如果您的模块使用了框架的 Logger,这些消息也会自动使用当前语言:

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

与 CLI i18n 的关系

CLI 拥有独立的国际化模块(ErisPulse.CLI.i18n),与框架核心的国际化模块完全解耦。

这种设计确保 CLI 的翻译变更不会影响框架核心的稳定性。