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

11 KiB
Raw Blame History

Gathering 系统多语言使用指南

本文档说明 Gathering 系统的多语言支持实现。

已完成的改动

1. src/classes/gathering/auction.py 重构

将硬编码的中文文本改为使用翻译函数,使拍卖会系统支持多语言。

1.1 添加类变量和 classmethodLLM Prompt

遵循 Action/MutualAction 系统的模式,为 LLM Prompt 添加统一的翻译支持:

修改后

@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() 方法

修改前

def get_info(self, world: "World") -> str:
    # TODO: Implement get_info
    return "拍卖会正在举行..."

修改后

def get_info(self, world: "World") -> str:
    from src.i18n import t
    return t("Auction is in progress...")

1.3 _generate_auction_events() 方法 - 事件内容

这是拍卖会生成事件记录的核心方法,有两种情况:有竞争和无竞争。

修改前

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]

修改后

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 收集信息并生成故事,包含多处需要翻译的文本。

成交信息和竞争信息

修改前

# 收集成交信息
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]})。")

修改后

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])
    )
物品信息格式

修改前

items_info_list.append(f"物品:{item.name},介绍:{info}")

修改后

items_info_list.append(
    t("Item: {item_name}, Description: {description}",
      item_name=item.name, description=info)
)
场景设定

修改前

gathering_info = (
    "事件类型:神秘拍卖会\n"
    "场景设定:拍卖会发生在一处神秘空间,由一位面目模糊、气息深不可测的神秘人主持。"
)

修改后

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."
)
标签文本

修改前

if items_info_str:
    details_list.append("【涉及拍品信息】")
    details_list.append(items_info_str)
    
details_list.append("\n【相关角色信息】")

修改后

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

修改前

story = await StoryTeller.tell_gathering_story(
    gathering_info=gathering_info,
    events_text=interaction_result,
    details_text=details_text,
    related_avatars=list(related_avatars),
    prompt="选取其中最有趣的一个侧面或一次竞价进行描写,无需面面俱到。"
)

修改后

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 - 故事生成提示词

使用示例

获取拍卖会状态

from src.classes.gathering.auction import Auction

auction = Auction()

# 获取拍卖会信息(自动翻译)
info = auction.get_info(world)
# 中文: "拍卖会正在举行..."
# 英文: "Auction is in progress..."

生成拍卖事件

# 拍卖会执行后会自动生成事件
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."

获取故事生成提示词

# 使用 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. 保持代码简洁

    • 避免过多的字符串拼接
    • 使用完整的句子模板而非片段
    • 优势:符合用户的代码风格要求

测试

# 切换语言
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 类型(如"宗门大比"),可以参考以下模板:

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 条翻译!🎉