Files
cultivation-world-simulator/tests/test_language.py
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

215 lines
8.8 KiB
Python
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.
import os
import pytest
from pathlib import Path
from src.classes.language import language_manager, LanguageType
from src.utils.config import CONFIG, update_paths_for_language
from src.utils.df import load_game_configs, reload_game_configs, game_configs
class TestLanguage:
def test_language_manager_defaults(self):
"""测试语言管理器默认状态"""
# 默认应该是 zh-CN
assert language_manager.current == LanguageType.ZH_CN
assert str(language_manager) == "zh-CN"
def test_language_manager_switch(self):
"""测试语言切换"""
language_manager.set_language("en-US")
assert language_manager.current == LanguageType.EN_US
assert str(language_manager) == "en-US"
# 测试无效语言回退
language_manager.set_language("invalid-lang")
assert language_manager.current == LanguageType.ZH_CN
def test_config_path_update(self):
"""测试路径更新逻辑"""
# 切到 en-US
language_manager.set_language("en-US")
update_paths_for_language("en-US")
expected_game_configs = Path("static/locales/en-US/game_configs")
# 注意Path 比较在不同系统上可能需要 resolve
assert CONFIG.paths.game_configs.resolve() == expected_game_configs.resolve()
assert CONFIG.paths.shared_game_configs.resolve() == Path("static/game_configs").resolve()
# 切回 zh-CN
language_manager.set_language("zh-CN")
update_paths_for_language("zh-CN")
expected_zh = Path("static/locales/zh-CN/game_configs")
assert CONFIG.paths.game_configs.resolve() == expected_zh.resolve()
def test_game_config_loading_and_override(self, tmp_path):
"""测试配置加载的合并与覆盖逻辑"""
# 1. 准备目录结构
shared_dir = tmp_path / "shared"
shared_dir.mkdir()
locales_dir = tmp_path / "locales"
zh_dir = locales_dir / "zh-CN" / "game_configs"
zh_dir.mkdir(parents=True)
# 2. 创建测试文件
# shared/common.csv - 只有共享有
(shared_dir / "common.csv").write_text("id,val\ndesc,val_desc\n1,common_val", encoding="utf-8")
# shared/override_me.csv - 共享有,将被覆盖
(shared_dir / "override_me.csv").write_text("id,val\ndesc,val_desc\n1,original_val", encoding="utf-8")
# locales/zh-CN/game_configs/override_me.csv - 覆盖版本
(zh_dir / "override_me.csv").write_text("id,val\ndesc,val_desc\n1,localized_val", encoding="utf-8")
# locales/zh-CN/game_configs/local_only.csv - 只有本地有
(zh_dir / "local_only.csv").write_text("id,val\ndesc,val_desc\n1,local_val", encoding="utf-8")
# 3. 临时修改 CONFIG.paths 指向测试目录
original_shared = CONFIG.paths.shared_game_configs
original_localized = CONFIG.paths.game_configs
try:
CONFIG.paths.shared_game_configs = shared_dir
CONFIG.paths.game_configs = zh_dir
# 4. 执行加载
loaded = load_game_configs()
# 5. 验证
# 验证 common.csv 存在
assert "common" in loaded
assert loaded["common"][0]["val"] == "common_val"
# 验证 override_me.csv 被覆盖
assert "override_me" in loaded
assert loaded["override_me"][0]["val"] == "localized_val"
# 验证 local_only.csv 存在
assert "local_only" in loaded
assert loaded["local_only"][0]["val"] == "local_val"
finally:
# 恢复配置
CONFIG.paths.shared_game_configs = original_shared
CONFIG.paths.game_configs = original_localized
def test_reload_game_configs_integration(self):
"""集成测试:测试 reload_game_configs 是否真的更新了全局变量"""
# 这个测试依赖于真实的 static 目录,只做简单检查
# 确保不会报错
try:
reload_game_configs()
# 至少应该有 sects 或者 region_map
assert "sect" in game_configs or "region_map" in game_configs
except Exception as e:
pytest.fail(f"reload_game_configs failed: {e}")
def test_i18n_objects_output(self):
"""测试对象输出的国际化"""
from src.classes.language import language_manager
from src.i18n import t, reload_translations
from src.classes.magic_stone import MagicStone
from src.classes.region import NormalRegion
from src.classes.persona import Persona
from src.classes.rarity import Rarity, RarityLevel
from src.classes.avatar.info_presenter import get_avatar_info
from src.classes.emotions import EmotionType
from src.classes.root import Root
from src.classes.appearance import get_appearance_by_level
from unittest.mock import MagicMock
# 切换到英文
language_manager.set_language("en-US")
reload_translations()
try:
# 1. MagicStone
ms = MagicStone(100)
assert str(ms) == "100 Spirit Stones"
# 2. Region
region = NormalRegion(id=1, name="TestRegion", desc="TestDesc")
assert "Normal Region" in str(region)
assert "Resource Distribution" in str(region)
# Distance check
assert "Distance" in region.get_info(current_loc=(0,0), step_len=1)
# 3. Persona
p = Persona(
id=1, key="TEST", name="TestPersona", desc="TestDesc",
exclusion_keys=[], rarity=Rarity(RarityLevel.N, 1.0, (255,255,255), "#FFFFFF", "Common"),
condition="", effects={}, effect_desc="TestEffect"
)
assert "Effect: TestEffect" in p.get_detailed_info()
# 4. Avatar Emotion
# Mock Avatar
avatar = MagicMock()
avatar.emotion = EmotionType.CALM
avatar.name = "TestAvatar"
avatar.gender = "Male"
avatar.age = MagicMock()
avatar.age.__str__.return_value = "20"
avatar.hp = MagicMock()
avatar.hp.__str__.return_value = "100/100"
avatar.magic_stone = MagicStone(0)
avatar.relations = {}
avatar.sect = None
avatar.alignment = None
avatar.root = MagicMock()
avatar.root.get_info.return_value = "Fire"
avatar.technique = None
avatar.cultivation_progress = MagicMock()
avatar.cultivation_progress.get_info.return_value = "Qi Refinement"
avatar.personas = []
avatar.materials = {}
avatar.appearance = MagicMock()
avatar.appearance.get_info.return_value = "Handsome"
avatar.weapon = None
avatar.auxiliary = None
avatar.long_term_objective = None
avatar.short_term_objective = None
avatar.nickname = None
avatar.spirit_animal = None
avatar.tile = MagicMock()
avatar.tile.region = None
info = get_avatar_info(avatar)
assert info["Emotion"] == "Calm"
# 5. Root (New check)
# The logic in format_root_cn removes "Root" suffix, so we expect "Thunder"
assert "Thunder" in Root.THUNDER.get_info()
assert "Water" in Root.THUNDER.get_info()
assert "Earth" in Root.THUNDER.get_info()
# 6. Appearance (New check)
app = get_appearance_by_level(7) # 7 is 俊美/Handsome
assert "Handsome" in app.get_info()
# 7. Cultivation Progress (New check)
from src.classes.cultivation import CultivationProgress
cp = CultivationProgress(1)
# In English, we expect space separated translated values
assert cp.get_info() == "Qi Refinement Early Stage"
# 切换回中文验证
language_manager.set_language("zh-CN")
reload_translations()
assert str(ms) == "100灵石"
assert "普通区域" in str(region)
assert "效果TestEffect" in p.get_detailed_info()
info_zh = get_avatar_info(avatar)
assert info_zh["情绪"] == "平静"
# 中文逻辑去掉了"灵根"后缀
assert "" in Root.THUNDER.get_info()
assert "俊美" in app.get_info()
# In Chinese, we now expect translated values with space
assert cp.get_info() == "练气 前期"
finally:
# Restore to default just in case
language_manager.set_language("zh-CN")
reload_translations()