From ea45b3c6260f24a6f640c8e9d6f1bbb96c79b2c2 Mon Sep 17 00:00:00 2001 From: DazedAnon Date: Mon, 27 Jul 2026 21:47:19 -0500 Subject: [PATCH] fix(rpgmaker): normalize English text and ignore editor comments in QA --- data/skills/rpgmaker_translation_qa.md | 7 +++++-- modules/rpgmakermvmz.py | 16 +++++++++++++++- tests/test_mvmz_db_original.py | 26 ++++++++++++++++++++++++++ tests/test_speaker_nameplate.py | 3 +++ tests/test_workflow_prompts.py | 1 + 5 files changed, 50 insertions(+), 3 deletions(-) diff --git a/data/skills/rpgmaker_translation_qa.md b/data/skills/rpgmaker_translation_qa.md index 7e2fbc2..a76fa4c 100644 --- a/data/skills/rpgmaker_translation_qa.md +++ b/data/skills/rpgmaker_translation_qa.md @@ -51,8 +51,9 @@ present. same path on the owning object. Numeric string keys in `_original` usually address list indexes in the live object, as in `System.json` lists and `terms` arrays. - For event code 102, pair each `_original[index]` choice with `parameters[0][index]`. -- For scalar event-command `_original` values, resolve the live display value by command shape: - - 401, 405, 408, 657, 356, 108: visible text in `parameters[0]`. +- For scalar event-command `_original` values, resolve the live value by command shape: + - 401, 405, 657, 356, 108: visible text in `parameters[0]`. + - 408: internal editor comment in `parameters[0]`; it is not player-facing text. - 101: visible name field in `parameters[4]`, or `parameters[0]` for the variable-name form. - 122: translated inner quoted/backticked string in `parameters[4]`, excluding its script wrapper and trailing semicolon. @@ -71,6 +72,8 @@ Check every resolvable pair for: counts, empty output, or accidental type changes. 2. Japanese or other source-language residue, unchanged source copied as translation, truncation, mojibake, model commentary, refusal text, Markdown fences, or JSON fragments inside player text. + Do not count unchanged code-408 editor comments as player-facing residue or actionable findings. + Report their coverage separately only when the user requests editor-facing localization. 3. Lost, added, duplicated, reordered, malformed, or altered runtime tokens. Include RPG Maker control codes such as `\C[n]`, `\N[n]`, `\V[n]`, `\I[n]`, `\{`, `\}`, `\.`, `\|`, `\!`, `\>`, `\<`, and `\^`; custom backslash codes; `__PROTECTED_n__`; printf-style placeholders; diff --git a/modules/rpgmakermvmz.py b/modules/rpgmakermvmz.py index 9359500..1c5bc70 100644 --- a/modules/rpgmakermvmz.py +++ b/modules/rpgmakermvmz.py @@ -928,6 +928,18 @@ def _apply_system_terms_message_original(data, key: str, raw: str) -> None: msg[key] = raw +def _normalize_system_message_translation(key: str, translated: str) -> str: + """Repair narrowly known English RPG Maker system-message failures.""" + text = str(translated or "").strip() + target = str(LANGUAGE or "").strip().casefold().replace("_", "-") + is_english = target in {"en", "eng", "english"} or target.startswith("en-") + if key == "escapeFailure" and is_english and re.fullmatch( + r"but\s+couldn['’]t\s+escape!?", text, flags=re.IGNORECASE + ): + return "But escape failed!" + return text + + _COLOR_SPEAKER_RE = re.compile( r"^[\\]+[cC]\[\d+\]【?(.+?)】?[\\]+[cC]\[\d+\](?:[\\]+[A-Za-z]+(?:\[[^\]]*\])?)*[\\]*$" ) @@ -4851,7 +4863,7 @@ def searchSystem(data, pbar): # Assign back translations to corresponding keys for n, key in enumerate(msg_keys[: len(tl_list)]): - translatedText = tl_list[n] + translatedText = _normalize_system_message_translation(key, tl_list[n]) for char in charList: translatedText = translatedText.replace(char, "") messages[key] = translatedText @@ -4914,6 +4926,8 @@ def _normalize_speaker_nameplate(translated: str) -> str: elif len(words) > 4: words = words[:3] out = " ".join(words).title().replace("'S", "'s") + # str.title() capitalizes after apostrophes ("Ma'am" -> "Ma'Am"). + out = re.sub(r"\bMa['’]Am\b", "Ma'am", out, flags=re.IGNORECASE) out = re.sub( r"(\d)(St|Nd|Rd|Th)\b", lambda m: m.group(1) + m.group(2).lower(), diff --git a/tests/test_mvmz_db_original.py b/tests/test_mvmz_db_original.py index 22bb6a2..a936cb4 100644 --- a/tests/test_mvmz_db_original.py +++ b/tests/test_mvmz_db_original.py @@ -279,6 +279,32 @@ class TestStatesOriginal(unittest.TestCase): class TestSystemOriginal(unittest.TestCase): + def test_normalizes_english_escape_failure(self): + original_language = mvmz.LANGUAGE + mvmz.LANGUAGE = "English" + try: + self.assertEqual( + mvmz._normalize_system_message_translation( + "escapeFailure", "But couldn't escape!" + ), + "But escape failed!", + ) + finally: + mvmz.LANGUAGE = original_language + + def test_does_not_rewrite_custom_escape_failure(self): + original_language = mvmz.LANGUAGE + mvmz.LANGUAGE = "English" + try: + self.assertEqual( + mvmz._normalize_system_message_translation( + "escapeFailure", "However, escape was impossible!" + ), + "However, escape was impossible!", + ) + finally: + mvmz.LANGUAGE = original_language + def test_first_pass_writes_original(self): data = json.loads((FIXTURES / "System_original_fixture.json").read_text(encoding="utf-8")) result, _ = _run_search_system(data) diff --git a/tests/test_speaker_nameplate.py b/tests/test_speaker_nameplate.py index 9f34fc7..fb6c618 100644 --- a/tests/test_speaker_nameplate.py +++ b/tests/test_speaker_nameplate.py @@ -24,6 +24,9 @@ class SpeakerNameplateNormalizeTests(unittest.TestCase): "Electrician Guy From", ) + def test_preserves_maam_casing(self): + self.assertEqual(_normalize_speaker_nameplate("ma'am"), "Ma'am") + if __name__ == "__main__": unittest.main() diff --git a/tests/test_workflow_prompts.py b/tests/test_workflow_prompts.py index ee8d547..40b0a99 100644 --- a/tests/test_workflow_prompts.py +++ b/tests/test_workflow_prompts.py @@ -41,6 +41,7 @@ class WorkflowTranslationPromptTests(unittest.TestCase): self.assertIn("mechanically check 100%", lowered) self.assertIn("deterministic stratified sample", lowered) self.assertIn("control-code scope and placement", lowered) + self.assertIn("do not count unchanged code-408 editor comments", lowered) self.assertIn("do not edit during the first pass", lowered) self.assertIn("stop and wait for approval", lowered) self.assertIn("never modify or remove `_original`", lowered)