重构i18n,现在game configs的配表,除了姓名这种外,都是统一的配表了。 对应的配表的名称和desc需要去i18n里取,但是其他配置不需要重复配置了。 这大大简化了之后新增i18n的心智负担。
230 lines
9.5 KiB
Python
230 lines
9.5 KiB
Python
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 setup_method(self):
|
||
# Reset language to default before each test
|
||
language_manager.set_language("zh-CN")
|
||
|
||
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")
|
||
|
||
# 重构后,game_configs 指向单一源 static/game_configs
|
||
expected_game_configs = Path("static/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/game_configs")
|
||
assert CONFIG.paths.game_configs.resolve() == expected_zh.resolve()
|
||
|
||
def test_game_config_loading_and_override(self, tmp_path):
|
||
"""测试配置加载的 I18n 翻译逻辑"""
|
||
# 1. 准备目录结构
|
||
# 现在不再有 shared/localized 分离,只有一个 game_configs 目录
|
||
game_configs_dir = tmp_path / "game_configs"
|
||
game_configs_dir.mkdir()
|
||
|
||
# 2. 创建测试文件 test.csv
|
||
# 包含 name_id/desc_id
|
||
csv_content = (
|
||
"id,name_id,name,desc_id,desc\n"
|
||
"名称ID,名称,描述ID,描述\n"
|
||
"test_item,TEST_ITEM_NAME,TestItemCN,TEST_ITEM_DESC,TestDescCN"
|
||
)
|
||
(game_configs_dir / "test.csv").write_text(csv_content, encoding="utf-8")
|
||
|
||
# 3. 临时修改 CONFIG.paths 指向测试目录
|
||
original_game_configs = CONFIG.paths.game_configs
|
||
original_shared = getattr(CONFIG.paths, "shared_game_configs", None)
|
||
original_localized = getattr(CONFIG.paths, "localized_game_configs", None)
|
||
|
||
try:
|
||
CONFIG.paths.game_configs = game_configs_dir
|
||
CONFIG.paths.shared_game_configs = game_configs_dir
|
||
CONFIG.paths.localized_game_configs = game_configs_dir
|
||
|
||
# Mock src.utils.df.t to simulate translation
|
||
# We need to patch 'src.utils.df.t'
|
||
from unittest.mock import patch
|
||
|
||
# Case 1: Translation exists (Simulating English)
|
||
with patch('src.utils.df.t') as mock_t:
|
||
mock_t.side_effect = lambda x: "TestItemEN" if x == "TEST_ITEM_NAME" else ("TestDescEN" if x == "TEST_ITEM_DESC" else x)
|
||
|
||
loaded = load_game_configs()
|
||
|
||
assert "test" in loaded
|
||
item = loaded["test"][0]
|
||
assert item["name"] == "TestItemEN"
|
||
assert item["desc"] == "TestDescEN"
|
||
|
||
# Case 2: No translation (Simulating Fallback/Chinese)
|
||
with patch('src.utils.df.t') as mock_t:
|
||
# If t returns the key itself (or empty), df.py keeps the original CSV value
|
||
mock_t.side_effect = lambda x: x
|
||
|
||
loaded = load_game_configs()
|
||
|
||
item = loaded["test"][0]
|
||
# Should fallback to CSV values
|
||
assert item["name"] == "TestItemCN"
|
||
assert item["desc"] == "TestDescCN"
|
||
|
||
finally:
|
||
# 恢复配置
|
||
CONFIG.paths.game_configs = original_game_configs
|
||
if original_shared:
|
||
CONFIG.paths.shared_game_configs = original_shared
|
||
if original_localized:
|
||
CONFIG.paths.localized_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()
|