# Action 多语言使用指南 本文档说明如何为 Action 类添加多语言支持。 ## 已完成的改动 ### 1. 基类增强 (`src/classes/action/action.py`) 在 `Action` 基类中添加了三个类方法和三个类变量: **类变量**(子类覆盖): ```python ACTION_NAME_ID: str = "" # 动作名称的 msgid DESC_ID: str = "" # 动作描述的 msgid REQUIREMENTS_ID: str = "" # 可执行条件的 msgid ``` **类方法**: ```python @classmethod def get_action_name(cls) -> str: """获取动作名称的翻译""" @classmethod def get_desc(cls) -> str: """获取动作描述的翻译""" @classmethod def get_requirements(cls) -> str: """获取可执行条件的翻译""" ``` ### 2. 示例实现 (`src/classes/action/assassinate.py`) 以 `Assassinate` 为例,展示了完整的多语言支持: ```python @cooldown_action class Assassinate(InstantAction, TargetingMixin): # 多语言 ID ACTION_NAME_ID = "assassinate_action_name" DESC_ID = "assassinate_description" REQUIREMENTS_ID = "assassinate_requirements" # 不需要翻译的常量 EMOJI = "🗡️" PARAMS = {"avatar_name": "AvatarName"} ACTION_CD_MONTHS = 12 # LLM 提示词 ID STORY_PROMPT_SUCCESS_ID = "assassinate_story_prompt_success" STORY_PROMPT_FAIL_ID = "assassinate_story_prompt_fail" # 自定义翻译方法(用于非标准字段) @classmethod def get_story_prompt_success(cls) -> str: from src.i18n import t return t(cls.STORY_PROMPT_SUCCESS_ID) @classmethod def get_story_prompt_fail(cls) -> str: from src.i18n import t return t(cls.STORY_PROMPT_FAIL_ID) ``` ## 使用方法 ### 方法一:使用基类提供的标准方法 适用于只有 `ACTION_NAME`、`DESC`、`REQUIREMENTS` 的 Action: ```python class MyAction(InstantAction): # 1. 设置 msgid ACTION_NAME_ID = "my_action_name" DESC_ID = "my_action_description" REQUIREMENTS_ID = "my_action_requirements" # 2. 其他代码保持不变 EMOJI = "⚔️" def _execute(self, **kwargs): # 实现逻辑 pass # 使用时: action_name = MyAction.get_action_name() # 自动翻译 desc = MyAction.get_desc() ``` ### 方法二:自定义翻译方法 适用于有额外字段需要翻译的 Action(如 LLM 提示词): ```python class MyAction(InstantAction): # 标准字段 ACTION_NAME_ID = "my_action_name" DESC_ID = "my_action_description" # 自定义字段 CUSTOM_PROMPT_ID = "my_action_custom_prompt" @classmethod def get_custom_prompt(cls) -> str: from src.i18n import t return t(cls.CUSTOM_PROMPT_ID) ``` ### 方法三:动态文本翻译 在运行时动态生成的文本(带占位符): ```python def start(self, target_name: str) -> Event: from src.i18n import t # 使用 t() 函数翻译并格式化 content = t("{avatar} starts attacking {target}!", avatar=self.avatar.name, target=target_name) return Event(self.world.month_stamp, content, ...) ``` ## 添加翻译 ### 1. 在 po 文件中添加条目 **英文** (`src/i18n/locales/en_US/LC_MESSAGES/messages.po`): ```po # Action: MyAction msgid "my_action_name" msgstr "My Action" msgid "my_action_description" msgstr "This is my action description" msgid "{avatar} starts attacking {target}!" msgstr "{avatar} starts attacking {target}!" ``` **中文** (`src/i18n/locales/zh_CN/LC_MESSAGES/messages.po`): ```po # Action: MyAction msgid "my_action_name" msgstr "我的动作" msgid "my_action_description" msgstr "这是我的动作描述" msgid "{avatar} starts attacking {target}!" msgstr "{avatar} 开始攻击 {target}!" ``` ### 2. 编译 po 文件 ```bash # Windows (需要安装 gettext) msgfmt src/i18n/locales/zh_CN/LC_MESSAGES/messages.po -o src/i18n/locales/zh_CN/LC_MESSAGES/messages.mo msgfmt src/i18n/locales/en_US/LC_MESSAGES/messages.po -o src/i18n/locales/en_US/LC_MESSAGES/messages.mo # macOS/Linux msgfmt src/i18n/locales/zh_CN/LC_MESSAGES/messages.po -o src/i18n/locales/zh_CN/LC_MESSAGES/messages.mo msgfmt src/i18n/locales/en_US/LC_MESSAGES/messages.po -o src/i18n/locales/en_US/LC_MESSAGES/messages.mo ``` ## 迁移现有 Action 对于现有的 Action(如 `Assassinate`),按以下步骤迁移: ### 步骤 1:替换类变量 **修改前**: ```python class OldAction(InstantAction): ACTION_NAME = "旧动作" DESC = "这是旧动作的描述" ``` **修改后**: ```python class OldAction(InstantAction): ACTION_NAME_ID = "old_action_name" DESC_ID = "old_action_description" ``` ### 步骤 2:更新引用 **修改前**: ```python # 在代码中直接使用 text = f"执行了{self.ACTION_NAME}" ``` **修改后**: ```python # 使用类方法获取 text = f"执行了{self.get_action_name()}" ``` ### 步骤 3:动态文本改用 t() **修改前**: ```python event_text = f"{self.avatar.name} 开始了动作" ``` **修改后**: ```python from src.i18n import t event_text = t("{avatar} started the action", avatar=self.avatar.name) ``` ## 最佳实践 1. **msgid 使用英文**:便于回退和调试 2. **msgid 具有描述性**:如 `assassinate_action_name` 而非 `act1` 3. **占位符使用具名参数**:`{avatar}` 而非 `%s` 4. **完整句子作为 msgid**:不要拼接字符串 5. **添加注释**:在 po 文件中标注每个 msgid 的用途 ## 测试 ```python # 切换语言 from src.classes.language import language_manager language_manager.set_language("zh-CN") # 测试翻译 from src.classes.action.assassinate import Assassinate print(Assassinate.get_action_name()) # 输出:暗杀 # 切换到英文 language_manager.set_language("en-US") print(Assassinate.get_action_name()) # 输出:Assassinate ``` ## MutualAction 多语言支持 ### 基类增强 (`src/classes/mutual_action/mutual_action.py`) **类变量**(子类覆盖): ```python ACTION_NAME_ID: str = "" DESC_ID: str = "" REQUIREMENTS_ID: str = "" STORY_PROMPT_ID: str = "" FEEDBACK_LABEL_IDS: dict[str, str] = {...} # 反馈标签 msgid 映射 ``` **类方法**: ```python @classmethod def get_feedback_label(cls, feedback_name: str) -> str: """获取反馈标签的翻译""" @classmethod def get_story_prompt(cls) -> str: """获取故事提示词的翻译""" ``` ### 示例实现 (`src/classes/mutual_action/dual_cultivation.py`) ```python @cooldown_action class DualCultivation(MutualAction): # 多语言 ID ACTION_NAME_ID = "dual_cultivation_action_name" DESC_ID = "dual_cultivation_description" REQUIREMENTS_ID = "dual_cultivation_requirements" STORY_PROMPT_ID = "dual_cultivation_story_prompt" # 不需要翻译的常量 EMOJI = "💕" PARAMS = {"target_avatar": "AvatarName"} FEEDBACK_ACTIONS = ["Accept", "Reject"] def start(self, target_avatar: "Avatar|str") -> Event: from src.i18n import t # 使用 t() 翻译动态文本 content = t("{initiator} invites {target} for dual cultivation", initiator=self.avatar.name, target=target_name) # ... ``` ### 反馈标签翻译 在 PO 文件中定义通用反馈标签: ```po # Feedback labels msgid "feedback_accept" msgstr "接受" / "Accept" msgid "feedback_reject" msgstr "拒绝" / "Reject" msgid "feedback_yield" msgstr "让步" / "Yield" ``` ## 已支持的 Action ### InstantAction & TimedAction - ✅ `Assassinate` - 暗杀 - ✅ `Attack` - 发起战斗 - ✅ `Breakthrough` - 突破 - ✅ `Buy` - 购买 - ✅ `Cast` - 铸造 - ✅ `Catch` - 御兽 - ✅ `Cultivate` - 修炼 - ✅ `DevourMortals` - 吞噬凡人 - ✅ `Escape` - 逃离 - ✅ `Harvest` - 采集 - ✅ `HelpMortals` - 帮助凡人 - ✅ `Hunt` - 狩猎 - ✅ `Mine` - 挖矿 - ✅ `Move` - 移动(基类) - ✅ `MoveAwayFromAvatar` - 远离角色 - ✅ `MoveAwayFromRegion` - 离开区域 - ✅ `MoveToAvatar` - 移动到角色 - ✅ `MoveToDirection` - 移动探索 - ✅ `MoveToRegion` - 移动到区域 - ✅ `NurtureWeapon` - 温养兵器 - ✅ `Play` - 消遣 - ✅ `PlunderMortals` - 搜刮凡人 - ✅ `Refine` - 炼丹 - ✅ `SelfHeal` - 疗伤 - ✅ `Sell` - 出售 ### MutualAction - ✅ `MutualAttack` - 攻击(互动版) - ✅ `Conversation` - 交谈 - ✅ `DriveAway` - 驱赶 - ✅ `DualCultivation` - 双修 - ✅ `Gift` - 赠送 - ✅ `Impart` - 传道 - ✅ `Occupy` - 抢夺洞府 - ✅ `Spar` - 切磋 - ✅ `Talk` - 攀谈 ## TODO - [x] 遍历所有 Action 并添加多语言支持 - [x] 为 MutualAction 添加类似的基类方法 - [x] 考虑为 LLM 提示词创建统一的辅助方法