fix(i18n): 修复 PROMPT_EXTENSIONS 元组声明、消除重复代码、优化锁策略

- fix: PROMPT_EXTENSIONS = (".prompt") 是字符串非元组,改为 (".prompt",)
- refactor: 将 extract_placeholders/format_template 统一到 loaders.py,
  消除 formatting.py、prompt_i18n.py、i18n_validate.py 三处重复
- perf: _get_catalog 和 load_prompt 改为双重检查锁定,I/O 不再阻塞其他线程
- perf: _log_once 使用独立 _warning_lock,不再与 _cache_lock 竞争
- fix: _scan_legacy_prompt_directory 添加 prompts_root 参数,修正 relative_to 语义
- refactor: 合并 _supported_prompt_files 两个变体为单函数 + recursive 参数
- docs: i18n.md 强化 repository-specific 校验策略标注,修正时间表述冗余
- fix: 验证脚本错误消息移除 Crowdin 暗示,标注为仓库级校验策略

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
春河晴
2026-03-13 01:25:29 +09:00
parent 8f7f31a164
commit 55eb911dd3
6 changed files with 65 additions and 85 deletions

View File

@@ -1,7 +1,6 @@
from __future__ import annotations
from pathlib import Path
from string import Formatter
import re
import sys
@@ -17,25 +16,16 @@ from src.common.i18n.loaders import ( # noqa: E402
get_locales_root,
load_locale_catalog,
)
from src.common.i18n.loaders import extract_placeholders # noqa: E402
from src.common.prompt_i18n import ( # noqa: E402
PROMPT_EXTENSIONS,
extract_prompt_placeholders,
get_prompts_root,
)
FORMATTER = Formatter()
HAN_CHARACTER_PATTERN = re.compile(r"[\u3400-\u4DBF\u4E00-\u9FFF\uF900-\uFAFF]")
def extract_placeholders(template: str) -> set[str]:
placeholders: set[str] = set()
for _, field_name, _, _ in FORMATTER.parse(template):
if not field_name:
continue
placeholders.add(field_name.split(".", maxsplit=1)[0].split("[", maxsplit=1)[0])
return placeholders
def contains_han_characters(text: str) -> bool:
return HAN_CHARACTER_PATTERN.search(text) is not None
@@ -65,7 +55,7 @@ def validate_locale_content(
source_text == target_text and contains_han_characters(source_text)
for source_text, target_text in zip(source_texts, target_texts, strict=False)
):
errors.append(f"[{locale}] key '{key}' 直接保留了包含中文字符的 source 文案,请通过 Crowdin 提供目标语言翻译")
errors.append(f"[{locale}] key '{key}' 直接保留了包含中文字符的 source 文案(仓库级校验策略),请提供目标语言翻译")
if locale_requires_latin_only_validation(locale) and any(contains_han_characters(text) for text in target_texts):
errors.append(f"[{locale}] key '{key}' 仍包含中文字符,请移除源语言残留后再提交")
@@ -218,6 +208,16 @@ def validate_prompt_templates(prompts_root: Path | None = None) -> tuple[list[st
return errors, warnings
def _print_warnings(warnings: list[str]) -> None:
if not warnings:
return
print(f"warnings ({len(warnings)}):")
for warning in warnings[:10]:
print(f" - {warning}")
if len(warnings) > 10:
print(f" - ... 另外还有 {len(warnings) - 10} 条 warning")
def main() -> int:
errors = validate_json_locales()
prompt_errors, prompt_warnings = validate_prompt_templates()
@@ -227,21 +227,11 @@ def main() -> int:
print("i18n validation failed:")
for error in errors:
print(f" - {error}")
if prompt_warnings:
print(f"warnings ({len(prompt_warnings)}):")
for warning in prompt_warnings[:10]:
print(f" - {warning}")
if len(prompt_warnings) > 10:
print(f" - ... 另外还有 {len(prompt_warnings) - 10} 条 warning")
_print_warnings(prompt_warnings)
return 1
print("i18n validation passed.")
if prompt_warnings:
print(f"warnings ({len(prompt_warnings)}):")
for warning in prompt_warnings[:10]:
print(f" - {warning}")
if len(prompt_warnings) > 10:
print(f" - ... 另外还有 {len(prompt_warnings) - 10} 条 warning")
_print_warnings(prompt_warnings)
return 0