fix(rpgmaker): normalize English text and ignore editor comments in QA

This commit is contained in:
DazedAnon 2026-07-27 21:47:19 -05:00
parent d38a8a2725
commit ea45b3c626
5 changed files with 50 additions and 3 deletions

View file

@ -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;

View file

@ -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(),

View file

@ -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)

View file

@ -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()

View file

@ -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)