Files
cultivation-world-simulator/docs/i18n-effect-usage.md
4thfever e1091fdf5a Feat/i18n (#92)
* feat: add vue-i18n

* feat: add vue-i18n

* feat: add vue-i18n

* feat: add language class

* add: en templates and configs

* add: en names

* refactor: name gender id and sect id

* feat(i18n): add gettext infrastructure for dynamic text translation (#81)

* feat(i18n): add gettext infrastructure for dynamic text translation

- Add src/i18n/ module with t() translation function
- Add .po/.mo files for zh_CN and en_US locales
- Update LanguageManager to reload translations on language change
- Add comprehensive tests (14 tests, all passing)
- Add implementation spec at docs/specs/i18n-dynamic-text.md

Phase 1 of i18n dynamic text implementation.

* feat(i18n): expand .po files with comprehensive translation entries

Add translation messages for:
- Battle result messages (fatal/non-fatal outcomes)
- Fortune event messages (item discovery, cultivation gains)
- Misfortune event messages (losses, damage, regression)
- Death reason messages
- Item exchange messages (equip, sell, discard)
- Single choice context and option labels
- Common labels (weapon, auxiliary, technique, elixir)

Both zh_CN and en_US locales updated with matching entries.

* test: add .po file integrity tests

* feat: i18n for actions

* feat: i18n for effects

* feat: i18n for gathering

* feat: i18n for classes

* feat: i18n for classes

* feat: i18n for classes

* feat: i18n for classes

* fix bugs

* fix bugs

* fix bugs

* fix bugs

* fix bugs

* fix bugs

* fix bugs

* fix bugs

* update csv

* update world info

* update prompt

* update prompt

* fix bug

* fix bug

* fix bug

* fix bug

* fix bug

* fix bug

* fix bug

* fix bug

* fix bug

* update

* update

* update

* update

* update

* update

* update

---------

Co-authored-by: Zihao Xu <xzhseh@gmail.com>
2026-01-24 13:47:23 +08:00

275 lines
7.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Effect 系统多语言使用指南
本文档说明 Effect 系统的多语言支持实现。
## 已完成的改动
### 1. `src/classes/effect/desc.py` 重构
将硬编码的中文映射字典改为使用翻译函数:
#### **Effect 名称翻译**
**修改前**
```python
EFFECT_DESC_MAP = {
"extra_max_hp": "最大生命值",
"extra_battle_strength_points": "战力点数",
# ... 30+ 项
}
```
**修改后**
```python
def get_effect_desc(effect_key: str) -> str:
"""获取 effect 的描述名称(支持国际化)"""
from src.i18n import t
msgid_map = {
"extra_max_hp": "effect_extra_max_hp",
"extra_battle_strength_points": "effect_extra_battle_strength_points",
# ...
}
msgid = msgid_map.get(effect_key, effect_key)
return t(msgid)
```
#### **Action 名称翻译**
**修改前**
```python
ACTION_DESC_MAP = {
"DualCultivation": "双修",
"DevourMortals": "吞噬凡人",
}
```
**修改后**
```python
def get_action_short_name(action_name: str) -> str:
"""获取 action 的简短名称(复用 Action 系统翻译)"""
from src.i18n import t
msgid = f"action_{action_name.lower()}_short_name"
return t(msgid)
```
#### **条件表达式翻译(简化方案)**
采用简化方案,保持代码形式,仅翻译前缀:
```python
def translate_condition(condition: str) -> str:
"""将代码形式的条件表达式转换为易读描述"""
from src.i18n import t
if not condition:
return t("Conditional effect")
# 特殊模式检测
if "avatar.personas" in condition and "any" in condition:
m = re.search(r'p\.name\s*==\s*["\'](.*?)["\']', condition)
if m:
return t("Has [{trait}] trait", trait=m.group(1))
# 保持代码形式
return t("When {condition}", condition=condition)
```
#### **Effect 文本格式化**
```python
def format_effects_to_text(effects: dict[str, Any] | list[dict[str, Any]]) -> str:
"""将 effects 字典转换为易读的文本描述"""
from src.i18n import t
# ... 处理逻辑 ...
# 使用翻译的分隔符
sep = t("effect_separator") # "" / "; "
text = sep.join(desc_list)
if effects.get("when"):
cond = translate_condition(str(effects["when"]))
return t("[{condition}] {effects}", condition=cond, effects=text)
return text
```
### 2. `src/classes/effect/mixin.py` 重构
修改 `get_effect_breakdown()` 方法,使用翻译函数:
**修改前**
```python
if self.sect:
_collect(f"宗门【{self.sect.name}", source_obj=self.sect)
if self.technique:
_collect(f"功法【{self.technique.name}", source_obj=self.technique)
_collect("灵根", source_obj=self.root)
# ...
```
**修改后**
```python
from src.i18n import t
if self.sect:
label = t("Sect [{name}]", name=self.sect.name)
_collect(label, source_obj=self.sect)
if self.technique:
label = t("Technique [{name}]", name=self.technique.name)
_collect(label, source_obj=self.technique)
_collect(t("Spirit Root"), source_obj=self.root)
# ...
```
## PO 文件新增条目
共新增约 **60+ 条翻译**
### Effect 名称28 项)
- `effect_extra_max_hp` - 最大生命值 / Max HP
- `effect_extra_battle_strength_points` - 战力点数 / Battle Strength
- `effect_extra_cultivate_exp` - 修炼经验 / Cultivation Experience
- ... 等 25 项
### Action 简短名称2 项)
- `action_dualcultivation_short_name` - 双修 / Dual Cultivation
- `action_devourmortals_short_name` - 吞噬凡人 / Devour Mortals
### 格式化相关4 项)
- `action_list_separator` - 、 / ,
- `effect_separator` - / ;
- `Special effect (dynamic)` - 特殊效果(动态)
- 条件翻译相关
### Effect 来源标签9 项)
- `Sect [{name}]` - 宗门【{name}】
- `Technique [{name}]` - 功法【{name}】
- `Spirit Root` - 灵根
- `Trait [{name}]` - 特质【{name}】
- `Weapon [{name}]` - 兵器【{name}】
- `Auxiliary [{name}]` - 辅助【{name}】
- `Spirit Animal [{name}]` - 灵兽【{name}】
- `Heaven and Earth Phenomenon` - 天地灵机
- `Elixir [{name}]` - 丹药【{name}】
## 使用示例
### 获取 Effect 描述
```python
from src.classes.effect.desc import get_effect_desc
# 自动根据当前语言返回翻译
desc = get_effect_desc("extra_max_hp")
# 中文: "最大生命值"
# 英文: "Max HP"
```
### 格式化 Effect 文本
```python
from src.classes.effect.desc import format_effects_to_text
effects = {
"extra_max_hp": 100,
"extra_battle_strength_points": 3
}
text = format_effects_to_text(effects)
# 中文: "最大生命值 +100战力点数 +3"
# 英文: "Max HP +100; Battle Strength +3"
```
### 带条件的 Effect
```python
effects = {
"extra_battle_strength_points": 3,
"when": "avatar.weapon.type == WeaponType.SWORD"
}
text = format_effects_to_text(effects)
# 中文: "[当avatar.weapon.type == WeaponType.SWORD] 战力点数 +3"
# 英文: "[When avatar.weapon.type == WeaponType.SWORD] Battle Strength +3"
```
### 获取 Effect 来源明细
```python
# 在 Avatar 类中自动使用翻译
breakdown = avatar.get_effect_breakdown()
# 返回: [
# ("宗门【天剑宗】", {"extra_battle_strength_points": 2}),
# ("功法【太玄真经】", {"extra_cultivate_exp": 50}),
# ("灵根", {"extra_max_hp": 30}),
# ...
# ]
```
## 设计决策
### ✅ 采用的简化方案
1. **条件表达式保持代码形式**
- 原因:条件表达式翻译复杂且容易出错
- 方案:仅翻译前缀"当" / "When",保持表达式本身为代码
- 优势:简单可靠,对开发者友好
2. **百分比和数值符号不翻译**
- 原因:`+`, `%`, `-` 等符号国际通用
- 方案:保持原样,节省翻译工作量
3. **Action 名称复用现有翻译**
- 原因Action 系统已完成国际化
- 方案:使用统一命名规则 `action_{name}_short_name`
- 优势:避免重复维护
### 🎯 核心改动点
- ✅ 2 个文件修改(`desc.py`, `mixin.py`
- ✅ 约 60+ 条新 msgid
- ✅ 难度较低,与 Action 系统模式一致
## 测试
```python
# 切换语言
from src.classes.language import language_manager
# 测试中文
language_manager.set_language("zh-CN")
from src.classes.effect.desc import get_effect_desc
print(get_effect_desc("extra_max_hp")) # 输出:最大生命值
# 测试英文
language_manager.set_language("en-US")
print(get_effect_desc("extra_max_hp")) # 输出Max HP
```
## 最佳实践
1. **新增 Effect 时同步添加翻译**
-`consts.py` 定义新 effect
-`desc.py``get_effect_desc()` 添加 msgid 映射
- 在 PO 文件添加中英文翻译
2. **保持命名规范**
- Effect msgid: `effect_{effect_key}`
- Action msgid: `action_{action_name}_short_name`
3. **测试多语言切换**
- 添加新翻译后测试切换语言是否正常显示
## 已完成
- ✅ 28 个 Effect 名称国际化
- ✅ 9 个 Effect 来源标签国际化
- ✅ 条件表达式简化翻译方案
- ✅ Action 名称复用现有系统
- ✅ 格式化函数国际化