Files
cultivation-world-simulator/docs/i18n-gathering-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

408 lines
11 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.
# Gathering 系统多语言使用指南
本文档说明 Gathering 系统的多语言支持实现。
## 已完成的改动
### 1. `src/classes/gathering/auction.py` 重构
将硬编码的中文文本改为使用翻译函数,使拍卖会系统支持多语言。
#### **1.1 添加类变量和 classmethodLLM Prompt**
遵循 Action/MutualAction 系统的模式,为 LLM Prompt 添加统一的翻译支持:
**修改后**
```python
@register_gathering
class Auction(Gathering):
"""拍卖会事件"""
# 类变量 - LLM Prompt
STORY_PROMPT_ID = "auction_story_prompt"
@classmethod
def get_story_prompt(cls) -> str:
"""获取故事生成提示词"""
from src.i18n import t
return t(cls.STORY_PROMPT_ID)
```
#### **1.2 get_info() 方法**
**修改前**
```python
def get_info(self, world: "World") -> str:
# TODO: Implement get_info
return "拍卖会正在举行..."
```
**修改后**
```python
def get_info(self, world: "World") -> str:
from src.i18n import t
return t("Auction is in progress...")
```
#### **1.3 _generate_auction_events() 方法 - 事件内容**
这是拍卖会生成事件记录的核心方法,有两种情况:有竞争和无竞争。
**修改前**
```python
if len(bids) >= 2:
runner_up = sorted_bids[1][0]
content = f"{item.name}的竞拍中,{winner.name}{deal_price} 灵石力压{runner_up.name}一头,将其收入囊中。"
related_avatars = [winner.id, runner_up.id]
else:
content = f"在拍卖会上,{winner.name}{deal_price} 灵石拍下了{item.name}"
related_avatars = [winner.id]
```
**修改后**
```python
from src.i18n import t
if len(bids) >= 2:
runner_up = sorted_bids[1][0]
content = t(
"In the auction for {item_name}, {winner_name} outbid {runner_up_name} with {price} spirit stones and won the item.",
item_name=item.name,
winner_name=winner.name,
runner_up_name=runner_up.name,
price=deal_price
)
related_avatars = [winner.id, runner_up.id]
else:
content = t(
"At the auction, {winner_name} acquired {item_name} for {price} spirit stones.",
winner_name=winner.name,
item_name=item.name,
price=deal_price
)
related_avatars = [winner.id]
```
#### **1.4 _generate_story() 方法 - 故事生成文本**
这个方法为 StoryTeller 收集信息并生成故事,包含多处需要翻译的文本。
##### **成交信息和竞争信息**
**修改前**
```python
# 收集成交信息
for item, (winner, deal_price) in deal_results.items():
interaction_lines.append(f"成交:{winner.name}{deal_price}灵石拍下{item.name}")
# 收集竞争信息
for item, bids in willing_prices.items():
if len(bids) < 2:
continue
sorted_bids = sorted(bids.items(), key=lambda x: x[1], reverse=True)
winner = sorted_bids[0][0]
runner_up = sorted_bids[1][0]
interaction_lines.append(f"竞争:在{item.name}的竞拍中,{winner.name}力压{runner_up.name}(出价{sorted_bids[1][1]})。")
```
**修改后**
```python
from src.i18n import t
# 收集成交信息
for item, (winner, deal_price) in deal_results.items():
interaction_lines.append(
t("Deal: {winner_name} acquired {item_name} for {price} spirit stones.",
winner_name=winner.name, item_name=item.name, price=deal_price)
)
# 收集竞争信息
for item, bids in willing_prices.items():
if len(bids) < 2:
continue
sorted_bids = sorted(bids.items(), key=lambda x: x[1], reverse=True)
winner = sorted_bids[0][0]
runner_up = sorted_bids[1][0]
interaction_lines.append(
t("Competition: In the auction for {item_name}, {winner_name} outbid {runner_up_name} (bid: {bid}).",
item_name=item.name, winner_name=winner.name,
runner_up_name=runner_up.name, bid=sorted_bids[1][1])
)
```
##### **物品信息格式**
**修改前**
```python
items_info_list.append(f"物品:{item.name},介绍:{info}")
```
**修改后**
```python
items_info_list.append(
t("Item: {item_name}, Description: {description}",
item_name=item.name, description=info)
)
```
##### **场景设定**
**修改前**
```python
gathering_info = (
"事件类型:神秘拍卖会\n"
"场景设定:拍卖会发生在一处神秘空间,由一位面目模糊、气息深不可测的神秘人主持。"
)
```
**修改后**
```python
gathering_info = t(
"Event Type: Mysterious Auction\nScene Setting: The auction takes place in a mysterious space, hosted by a mysterious figure with an unfathomable aura."
)
```
##### **标签文本**
**修改前**
```python
if items_info_str:
details_list.append("【涉及拍品信息】")
details_list.append(items_info_str)
details_list.append("\n【相关角色信息】")
```
**修改后**
```python
if items_info_str:
details_list.append(t("【Auction Items Information】"))
details_list.append(items_info_str)
details_list.append(t("\n【Related Avatars Information】"))
```
##### **LLM Prompt**
**修改前**
```python
story = await StoryTeller.tell_gathering_story(
gathering_info=gathering_info,
events_text=interaction_result,
details_text=details_text,
related_avatars=list(related_avatars),
prompt="选取其中最有趣的一个侧面或一次竞价进行描写,无需面面俱到。"
)
```
**修改后**
```python
story = await StoryTeller.tell_gathering_story(
gathering_info=gathering_info,
events_text=interaction_result,
details_text=details_text,
related_avatars=list(related_avatars),
prompt=self.get_story_prompt()
)
```
---
## PO 文件新增条目
共新增约 **10 条翻译**
### 状态文本1 项)
- `Auction is in progress...` - 拍卖会正在举行... / Auction is in progress...
### 事件内容2 项)
- `In the auction for {item_name}, {winner_name} outbid {runner_up_name} with {price} spirit stones and won the item.` - 有竞争的拍卖
- `At the auction, {winner_name} acquired {item_name} for {price} spirit stones.` - 无竞争的拍卖
### 故事生成文本3 项)
- `Deal: {winner_name} acquired {item_name} for {price} spirit stones.` - 成交信息
- `Competition: In the auction for {item_name}, {winner_name} outbid {runner_up_name} (bid: {bid}).` - 竞争信息
- `Item: {item_name}, Description: {description}` - 物品信息格式
### 场景设定1 项)
- `Event Type: Mysterious Auction\nScene Setting: ...` - 神秘拍卖会场景设定
### 标签2 项)
- `【Auction Items Information】` - 拍品信息标签
- `\n【Related Avatars Information】` - 角色信息标签
### LLM Prompt1 项)
- `auction_story_prompt` - 故事生成提示词
---
## 使用示例
### 获取拍卖会状态
```python
from src.classes.gathering.auction import Auction
auction = Auction()
# 获取拍卖会信息(自动翻译)
info = auction.get_info(world)
# 中文: "拍卖会正在举行..."
# 英文: "Auction is in progress..."
```
### 生成拍卖事件
```python
# 拍卖会执行后会自动生成事件
events = await auction.execute(world)
# 有竞争的拍卖事件
# 中文: "在玄铁剑的竞拍中,李云以 1500 灵石力压王峰一头,将其收入囊中。"
# 英文: "In the auction for Mystic Iron Sword, Li Yun outbid Wang Feng with 1500 spirit stones and won the item."
# 无竞争的拍卖事件
# 中文: "在拍卖会上,李云以 800 灵石拍下了破虚丹。"
# 英文: "At the auction, Li Yun acquired Void-Breaking Pill for 800 spirit stones."
```
### 获取故事生成提示词
```python
# 使用 classmethod 获取翻译后的 prompt
prompt = Auction.get_story_prompt()
# 中文: "选取其中最有趣的一个侧面或一次竞价进行描写,无需面面俱到。"
# 英文: "Select the most interesting aspect or bidding moment to describe, no need to cover everything."
```
---
## 设计决策
### ✅ 采用的方案
1. **与 Action/MutualAction 系统保持一致**
- 使用 `t()` 翻译函数
- LLM Prompt 使用类变量 + classmethod 模式
- 占位符格式化字符串
- 优势:统一的代码风格,易于维护
2. **事件内容完全国际化**
- 所有事件文本使用 `t()` 和占位符
- 支持不同语言的语序和表达习惯
- 优势:翻译灵活,自然流畅
3. **故事生成相关文本统一处理**
- 交互结果文本使用固定模板
- 标签文本使用翻译函数
- LLM Prompt 可切换语言
- 优势StoryTeller 可以使用相应语言生成故事
4. **保持代码简洁**
- 避免过多的字符串拼接
- 使用完整的句子模板而非片段
- 优势:符合用户的代码风格要求
---
## 测试
```python
# 切换语言
from src.classes.language import language_manager
# 测试中文
language_manager.set_language("zh-CN")
auction = Auction()
print(auction.get_info(world)) # 输出:拍卖会正在举行...
print(Auction.get_story_prompt()) # 输出中文提示词
# 测试英文
language_manager.set_language("en-US")
print(auction.get_info(world)) # 输出Auction is in progress...
print(Auction.get_story_prompt()) # 输出英文提示词
```
---
## 最佳实践
1. **新增 Gathering 类型时同步添加翻译**
- 如果添加新的 Gathering 实现(如"宗门大比"、"秘境开启"
- 遵循相同的模式:类变量 + classmethod
- 在 PO 文件添加对应翻译
2. **保持命名规范**
- LLM Prompt msgid 格式:`{gathering_type}_story_prompt`
- 事件内容使用完整句子作为 msgid
- 标签使用描述性文本作为 msgid
3. **格式化字符串命名**
- 使用描述性占位符名称(如 `{winner_name}`, `{item_name}`, `{price}`
- 保持占位符在中英文翻译中一致
4. **测试多语言切换**
- 添加新翻译后测试切换语言是否正常显示
- 检查事件内容和故事生成是否符合语言习惯
---
## 已完成
- ✅ 1 个文件修改(`auction.py`
- ✅ 约 10 条新翻译状态、事件、故事文本、标签、Prompt
- ✅ 完全国际化的拍卖会系统
- ✅ LLM Prompt 支持多语言
- ✅ 统一的代码模式
---
## 影响范围
Gathering 系统的本地化影响以下功能:
1. **拍卖会信息展示** - `get_info()`
2. **拍卖事件生成** - `_generate_auction_events()`
3. **故事生成** - `_generate_story()`
4. **LLM Prompt** - `get_story_prompt()`
所有这些功能现在都完全支持多语言切换!
---
## 扩展性
### 未来添加新 Gathering 类型的模板
如果需要添加新的 Gathering 类型(如"宗门大比"),可以参考以下模板:
```python
from src.classes.gathering.gathering import Gathering, register_gathering
from src.i18n import t
@register_gathering
class SectCompetition(Gathering):
"""宗门大比事件"""
# 类变量
STORY_PROMPT_ID = "sect_competition_story_prompt"
@classmethod
def get_story_prompt(cls) -> str:
"""获取故事生成提示词"""
return t(cls.STORY_PROMPT_ID)
def get_info(self, world: "World") -> str:
return t("Sect competition is underway...")
# ... 其他方法使用 t() 翻译所有文本
```
然后在 PO 文件添加对应翻译即可。
---
## 总结
Gathering 系统的本地化采用了与 Action/MutualAction 一致的模式,工作量小、易于维护、扩展性好。这是项目中最简单的本地化任务之一,只涉及 1 个文件和约 10 条翻译!🎉