國際化 (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()
語言檢測機制
框架按以下優先級檢測用戶語言:
- 環境變數
ERISPULSE_LANG— 最高優先級,用於測試和臨時切換 - Windows API —
GetUserDefaultLocaleName(僅 Windows,不受 Git Bash 等工具覆蓋LANG的影響) - 環境變數 —
LANGUAGE>LC_ALL>LC_MESSAGES>LANG(Unix/macOS 標準) - 系統 Locale —
locale.getlocale()/locale.getdefaultlocale() - 兜底 — en(英文)
最近映射原則
當檢測到的語言不是精確匹配時,按最近原則映射到支援的語言:
zh-TW,zh-HK,zh-MO,zh-Hant→ 繁體中文- 其他所有
zh-*(如zh-CN,zh-SG)→ 簡體中文 en-US,en-GB,en-AU等 → 英文ja-JP→ 日文ru-RU→ 俄文- 其他未識別語言 → 簡體中文(兜底)
在模組中使用 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 的鍵路徑規則
- 預設:使用
<模組註冊名>.<屬性名>作為完整鍵路徑- 範例:模組名為
MyModule,屬性welcome→ 鍵路徑MyModule.welcome
- 範例:模組名為
- 顯式:透過
I18nKey(key="...")參數指定任意點分路徑- 適合深層嵌套的鍵名(如
mymodule.config.basic.token)
- 適合深層嵌套的鍵名(如
在適配器中使用
適配器同樣支援 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__ 階段(即配置模板生成之前)自動註冊,確保配置描述引用的 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 起):
- 模板生成脫敏:
dataclass_to_toml_with_comments()生成配置模板時,secret 字段的真實值不會寫入檔案(顯示為空占位),避免敏感資訊落盤 - 通用脫敏工具:
redact_secret(value)將非空值替換為***,空值原樣返回,可用於日誌輸出等場景
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):
key— 翻譯鍵(僅位置參數,不與**kwargs中的key=衝突)default— 翻譯不存在時返回的預設值,預設為None(返回鍵名本身)**kwargs— 格式化參數,用於填充翻譯值中的{placeholder}
示例:
# 翻譯定義: "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),與框架核心的國際化模組完全解耦。
- Core i18n — 框架核心模組使用,外部模組可註冊翻譯
- CLI i18n — 命令列介面內部使用,不與 Core 共享翻譯資料
這種設計確保 CLI 的翻譯變更不會影響框架核心的穩定性。