This commit is contained in:
2026-10-25 10:30:22 +08:00
parent 38894ba80c
commit ae84347912
11 changed files with 287 additions and 12 deletions

View File

@@ -14,6 +14,7 @@ import requests
logger = logging.getLogger(__name__)
_JSON_FENCE_RE = re.compile(r"^```(?:json)?\s*|\s*```$", re.IGNORECASE)
_CJK_RE = re.compile("[\u3400-\u4dbf\u4e00-\u9fff\uf900-\ufaff]")
_NUMBERED_LINE_RE = re.compile(r"^\s*[\[(]?(\d+)[\])]?[\.、::\)]\s*(.+?)\s*$")
_TRANSLATE_BATCH_SENTENCES = 12
_TRANSLATE_BATCH_CHARS = 2_200
@@ -95,10 +96,13 @@ class AITutor:
raise AITutorError("No sentence text was provided for translation.")
glossary = _normalize_translation_terms(terms)
source_by_index = {index: text for index, text in ordered}
translations = {
index: text.strip()
for index, text in (existing or {}).items()
if isinstance(text, str) and text.strip()
if isinstance(text, str)
and text.strip()
and not translation_needs_repair(source_by_index.get(index, ""), text)
}
pending = [(index, text) for index, text in ordered if index not in translations]
if not pending:
@@ -304,10 +308,14 @@ class AITutor:
timeout_seconds=max(self.timeout_seconds, _TRANSLATE_TIMEOUT_SECONDS),
)
parsed = _parse_translation_payload(content, expected_indexes)
missing = [index for index in expected_indexes if not parsed.get(index, "").strip()]
source_by_index = {index: text for index, text in batch}
missing = [
index
for index in expected_indexes
if translation_needs_repair(source_by_index.get(index, ""), parsed.get(index, ""))
]
if missing:
raise AITutorError("AI translation returned an unexpected response.")
source_by_index = {index: text for index, text in batch}
return {
index: _apply_translation_terms(source_by_index.get(index, ""), parsed[index], glossary)
for index in expected_indexes
@@ -377,6 +385,19 @@ class AITutor:
return content.strip()
def translation_needs_repair(source: str | None, zh_text: str | None) -> bool:
"""Return True when a sentence still needs a Chinese translation."""
source_text = (source or "").strip()
if not source_text:
return False
zh = (zh_text or "").strip()
if not zh:
return True
if _CJK_RE.search(zh):
return False
return _CJK_RE.search(source_text) is None
def _translation_batches(
pending: list[tuple[int, str]],
all_sentences: list[tuple[int, str]],