i18n: localize prompt templates with crowdin

This commit is contained in:
春河晴
2026-03-13 00:09:01 +09:00
parent 68f0ac3803
commit c9bfedddd2
46 changed files with 1156 additions and 17 deletions

View File

@@ -0,0 +1,129 @@
from __future__ import annotations
from pathlib import Path
import pytest
from src.common.i18n import set_locale
from src.common.prompt_i18n import clear_prompt_cache, load_prompt, list_prompt_templates
from src.prompt.prompt_manager import PromptManager
@pytest.fixture(autouse=True)
def clear_prompt_i18n_cache() -> None:
set_locale("zh-CN")
clear_prompt_cache()
yield
clear_prompt_cache()
set_locale("zh-CN")
def write_prompt(prompt_dir: Path, locale: str | None, name: str, content: str) -> None:
base_dir = prompt_dir if locale is None else prompt_dir / locale
base_dir.mkdir(parents=True, exist_ok=True)
(base_dir / f"{name}.prompt").write_text(content, encoding="utf-8")
def test_load_prompt_prefers_requested_locale(tmp_path: Path) -> None:
prompts_root = tmp_path / "prompts"
write_prompt(prompts_root, "zh-CN", "replyer", "你好,{user_name}")
write_prompt(prompts_root, "en-US", "replyer", "Hello, {user_name}")
rendered = load_prompt("replyer", locale="en-US", prompts_root=prompts_root, user_name="Mai")
assert rendered == "Hello, Mai"
def test_load_prompt_falls_back_to_default_locale(tmp_path: Path) -> None:
prompts_root = tmp_path / "prompts"
write_prompt(prompts_root, "zh-CN", "replyer", "你好,{user_name}")
rendered = load_prompt("replyer", locale="en-US", prompts_root=prompts_root, user_name="Mai")
assert rendered == "你好Mai"
def test_load_prompt_falls_back_to_legacy_root(tmp_path: Path) -> None:
prompts_root = tmp_path / "prompts"
write_prompt(prompts_root, None, "replyer", "Legacy {user_name}")
rendered = load_prompt("replyer", locale="en-US", prompts_root=prompts_root, user_name="Mai")
assert rendered == "Legacy Mai"
def test_load_prompt_with_category_falls_back_to_legacy_root(tmp_path: Path) -> None:
prompts_root = tmp_path / "prompts"
write_prompt(prompts_root, None, "replyer", "Legacy {user_name}")
rendered = load_prompt("replyer", locale="en-US", category="chat", prompts_root=prompts_root, user_name="Mai")
assert rendered == "Legacy Mai"
def test_load_prompt_strict_mode_raises_on_missing_placeholder(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
prompts_root = tmp_path / "prompts"
write_prompt(prompts_root, "zh-CN", "replyer", "你好,{user_name},现在是 {current_time}")
monkeypatch.setenv("MAIBOT_PROMPT_I18N_STRICT", "1")
with pytest.raises(KeyError) as exc_info:
load_prompt("replyer", locale="zh-CN", prompts_root=prompts_root, user_name="Mai")
assert "current_time" in str(exc_info.value)
def test_load_prompt_rejects_path_traversal(tmp_path: Path) -> None:
prompts_root = tmp_path / "prompts"
write_prompt(prompts_root, "zh-CN", "replyer", "你好")
with pytest.raises(ValueError):
load_prompt("../replyer", locale="zh-CN", prompts_root=prompts_root)
def test_list_prompt_templates_prefers_locale_specific_files(tmp_path: Path) -> None:
prompts_root = tmp_path / "prompts"
write_prompt(prompts_root, None, "replyer", "Legacy")
write_prompt(prompts_root, "zh-CN", "replyer", "中文")
write_prompt(prompts_root, "en-US", "replyer", "English")
set_locale("en-US")
prompt_templates = list_prompt_templates(prompts_root=prompts_root)
assert prompt_templates["replyer"].read_text(encoding="utf-8") == "English"
def test_list_prompt_templates_reports_duplicate_name_with_custom_root(tmp_path: Path) -> None:
prompts_root = tmp_path / "prompts"
first_dir = prompts_root / "zh-CN" / "chat"
second_dir = prompts_root / "zh-CN" / "system"
first_dir.mkdir(parents=True, exist_ok=True)
second_dir.mkdir(parents=True, exist_ok=True)
(first_dir / "replyer.prompt").write_text("chat", encoding="utf-8")
(second_dir / "replyer.prompt").write_text("system", encoding="utf-8")
with pytest.raises(ValueError) as exc_info:
list_prompt_templates(prompts_root=prompts_root)
assert "zh-CN/chat/replyer.prompt" in str(exc_info.value)
assert "zh-CN/system/replyer.prompt" in str(exc_info.value)
def test_prompt_manager_load_prompts_prefers_locale_dir(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
prompts_root = tmp_path / "prompts"
custom_prompts_root = tmp_path / "data" / "custom_prompts"
custom_prompts_root.mkdir(parents=True, exist_ok=True)
write_prompt(prompts_root, "zh-CN", "replyer", "中文模板")
write_prompt(prompts_root, "en-US", "replyer", "English template")
set_locale("en-US")
monkeypatch.setattr("src.prompt.prompt_manager.PROMPTS_DIR", prompts_root, raising=False)
monkeypatch.setattr("src.prompt.prompt_manager.CUSTOM_PROMPTS_DIR", custom_prompts_root, raising=False)
monkeypatch.setattr("src.prompt.prompt_manager.SUFFIX_PROMPT", ".prompt", raising=False)
manager = PromptManager()
manager.load_prompts()
assert manager.get_prompt("replyer").template == "English template"