国際化 (i18n) システム
ErisPulse v2.5.0 以降、完全な国際化 (i18n) 機能が内蔵されています。フレームワークのコアおよび 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.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() を呼び出す必要がある | フレームワークが統一されたドメインで登録する |
I18nClass のキー経路ルール
- デフォルト:
<モジュール登録名>.<属性名>を完全なキー経路として使用- 例:モジュール名が
MyModule、属性welcome→ キー経路MyModule.welcome
- 例:モジュール名が
- 明示的:
I18nKey(key="...")パラメータを使って任意のドット区切り経路を指定- 深層ネストされたキー名(例:
mymodule.config.basic.token)に適している
- 深層ネストされたキー名(例:
アダプターでの使用
アダプターも 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 フィールドは {"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)) # 2つのエラー:列挙制約と範囲制約
設定の翻訳登録
設定フィールドの 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は互換性のための shims として残されています。
推奨は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は言語に依存しないデフォルトテキストであり、どの言語にも登録されません。
翻訳を有効にするには、少なくとも1つの言語パラメータ(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 の翻訳の変更がフレームワークのコアの安定性に影響を与えることがありません。