diff --git a/data/skills/rpgmaker_translation_qa.md b/data/skills/rpgmaker_translation_qa.md new file mode 100644 index 0000000..7e2fbc2 --- /dev/null +++ b/data/skills/rpgmaker_translation_qa.md @@ -0,0 +1,159 @@ +# QA Exported RPG Maker Translations + + +Audit the translated RPG Maker JSON files in this detected game data folder: + +`{{GAME_DATA_FOLDER}}` + +The preserved Japanese source attached to each translated value as `_original` is authoritative +for source-versus-translation comparisons. Inspect every JSON file in this folder that contains +`_original`; use other JSON records in the same folder for structure and nearby event context. + +Use the rest of this game folder as read-only context for scripts, plugins, event flow, assets, +per-game skills, and any other evidence needed to understand how text appears at runtime: + +`{{GAME_ROOT}}` + +Use this glossary as authoritative for names and terminology: + +`{{VOCAB_FILE}}` + +This is a post-export QA review of the playable game data. During the first pass, do not edit any +translation or modify any game file. Start immediately with the audit; do not ask the user to +choose files unless no translated JSON containing `_original` can be found. + + +## Required review model + +Do not try to read thousands of raw lines sequentially into context. Build a compact inventory of +every `_original` leaf and its current translated counterpart, then combine exhaustive mechanical +checks with targeted semantic review. + +- Mechanically check 100% of resolvable source/translation pairs. +- Deduplicate identical source/translation pairs and repeated issue signatures. +- Risk-score the remaining pairs and semantically review the highest-risk clusters first. +- Add a deterministic stratified sample across every file, event code or database field, speaker, + and short/medium/long text band. Prefer at least one representative from every stratum; cap a + routine first-pass semantic sample near 500 unique pairs unless severe problems justify more. +- Review nearby event commands together when meaning, speaker, referent, or control-code placement + depends on context. +- State exact coverage. Never imply that sampled semantic review covered every line. + +Use a temporary script or compact index when useful. Do not leave generated QA artifacts in the +game data folder or elsewhere in the game folder. + +## Pair extraction + +Preserve a stable locator for every pair: relative filename plus JSON path, and event code when +present. + +- For database objects with an `_original` object, pair each string leaf with the value at the + 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]`. + - 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. + - 320, 324, 325: visible value in `parameters[1]`. +- A scalar `_original` on the first command of a merged 401/405/408 group may represent multiple + live lines. Rejoin the contiguous translated display commands before comparing it; do not report + the continuation commands as missing translations. +- If a pair cannot be resolved safely, report an extractor/shape warning. Do not guess or mutate + that record. + +## Exhaustive mechanical checks + +Check every resolvable pair for: + +1. Invalid JSON, damaged command/list/object structure, missing live counterparts, wrong choice + 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. +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; + interpolation; and meaningful HTML/plugin tags. +4. Control-code scope and placement, not just token counts. Color/font openers and resets must wrap + the translated equivalent of the same source span. Name colors must remain around the name; + icon and variable codes must stay beside the phrase they modify; waits and pauses must retain + their intended beat. Do not demand identical character offsets because English length differs. +5. Suspicious length ratios, repeated generic output, many unrelated sources collapsed to one + translation, one source translated inconsistently, broken speaker tags, inconsistent glossary + terms, changed numbers, polarity/negation risk, pronoun or subject flips, and punctuation or + quote damage. +6. Likely display overflow using the configured wrap widths when available. Distinguish an actual + overlong display line from intentional newlines or non-dialogue script text. + +Treat token spelling case-insensitively only where RPG Maker itself does; preserve the spelling and +escaping already used by the file. Do not flag natural English word order merely because an inline +token moved, provided it still modifies the same semantic phrase and its runtime order is safe. + +## Targeted semantic review + +Compare Japanese and English for fidelity, fluency, tone, and context. Prioritize candidates with: + +- Negation, conditionals, quantities, dates, choices, objectives, or gameplay instructions. +- Pronouns, omitted subjects, kinship, speaker changes, or third-person self-reference. +- Names and terms missing from or conflicting with the glossary. +- Very short ambiguous Japanese, unusually large length changes, or awkward literal English. +- Sex, violence, comedy, dialect, honorifics, emotional intensity, and other register-sensitive + language. +- Mechanical warnings, inconsistent translation clusters, or context-dependent control codes. + +Use the source and surrounding event context as evidence. Do not call a translation wrong solely +because another valid wording is possible. Separate definite defects from subjective polish. + +## First-pass output and approval gate + +Do not edit during the first pass. Return these sections: + +### QA coverage + +- Files found / files parsed +- `_original` leaves found +- Pairs mechanically checked and unresolved pairs +- Unique pairs/clusters +- Pairs semantically reviewed, sampling method, and strata represented +- Any blind spots + +### Findings summary + +Count findings by severity and category: + +- **Critical**: invalid JSON/structure or runtime-breaking token corruption. +- **High**: clear mistranslation, missing content, wrong control-code scope, source residue, or + glossary/name failure. +- **Medium**: likely context, consistency, fluency, tone, or overflow problem needing judgment. +- **Low**: optional polish only. + +### Findings requiring action + +Show a compact table with stable IDs, severity, file + JSON path, event code/field, short original, +current translation, issue, and proposed correction. Include every Critical and High finding when +practical. If many share one cause, group them, show representative locators and the total affected +count. Limit Medium/Low examples to the most useful representatives. Never dump whole JSON files. + +### Recommendation + +Say whether playtesting/release should be blocked, conditionally allowed after listed fixes, or +allowed with only optional polish remaining. End with one focused approval question offering to +apply all Critical/High-confidence fixes, selected finding IDs, or no edits. + +Stop and wait for approval. + +## After approval + +Edit only the approved live translated values under `{{GAME_DATA_FOLDER}}`. + +- Never modify or remove `_original`. +- Preserve JSON types, event commands, non-text fields, control codes, placeholders, indentation, + and encoding. Make the smallest possible changes. +- Follow the glossary and nearby context. Do not rewrite acceptable lines outside the approved + scope. +- Reparse every touched JSON file, rerun all mechanical checks on touched records, and check that + no new Japanese residue or token mismatch was introduced. +- Report files and finding IDs fixed, remaining risks, and final game-data QA readiness. +- Do not modify anything outside the detected game data folder. The rest of the game is context + only. diff --git a/gui/skills_tab.py b/gui/skills_tab.py index 964ca78..6e4855c 100644 --- a/gui/skills_tab.py +++ b/gui/skills_tab.py @@ -118,6 +118,16 @@ class SkillsTab(QWidget): hint="VX Ace Ruby script localisation audit and approved in-place translation prompt.", is_json=False, ) + self._add_file_page( + key="rpgmaker_translation_qa", + tab_title="Translation QA", + path=SKILLS_DIR / "rpgmaker_translation_qa.md", + hint=( + "Post-export RPG Maker game-data audit. Keep the {{GAME_DATA_FOLDER}}, " + "{{GAME_ROOT}}, and {{VOCAB_FILE}} placeholders." + ), + is_json=False, + ) self._add_file_page( key="image_translation", tab_title="Image TL", diff --git a/gui/workflow_tab.py b/gui/workflow_tab.py index 6bba9e0..3a315ef 100644 --- a/gui/workflow_tab.py +++ b/gui/workflow_tab.py @@ -8,7 +8,7 @@ Provides a guided, step-by-step interface: Step 2 – Setup: speaker flags, Project Setup skill, vocab / quirks / game skill Step 3 – Translation: Phase 0 (DB), Phase 1 (dialogue), Phase 1b (111 cache) Step 4 – Translation Phase 2 (risky codes) - Step 5 – Plugins.js prompt helpers + export translated/ to the game + Step 5 – Plugins.js prompt helpers + translation QA + export translated/ to the game Step 6 – Prepare and translate editable bitmap UI images Step 7 – Install TL Inspector and/or Forge playtest plugins """ @@ -524,7 +524,10 @@ _STEP_HELP: dict[int, str] = { "Export - copy finished translations from translated/ back " "into the game's data folder:
" "• Export Active Files - only names currently in files/
" - "• Export ALL - everything under translated/

" + "• Export ALL - everything under translated/
" + "• Then run QA Game Data against the exported files. It compares every preserved " + "_original source with its translation, reports prioritized findings, and " + "waits for approval before editing the game data

" "Create Public Release ZIP packages the complete game beside its folder while " "omitting translator workspaces, VCS metadata, documentation, backups, saves, and " "other tool artifacts. GameUpdate files and all installed plugins are kept." @@ -2245,6 +2248,15 @@ class WorkflowTab(QWidget): inner.addLayout(_labeled_row(export_lbl, export_active_btn, export_all_btn)) + qa_lbl = QLabel("QA") + qa_btn = _make_btn("🔎 QA Game Data Skill", "#8a6d3b") + qa_btn.setToolTip( + "After export, copy a scalable QA skill for the detected game data folder. " + "It checks every _original pair, reviews prioritized samples, and asks before fixes." + ) + qa_btn.clicked.connect(self._copy_translation_qa_prompt) + inner.addLayout(_labeled_row(qa_lbl, qa_btn)) + release_lbl = QLabel("Release") self._release_zip_btn = _make_btn("📦 Create Public Release ZIP", "#007acc") self._release_zip_btn.setToolTip( @@ -3766,6 +3778,36 @@ class WorkflowTab(QWidget): "Risky codes analysis prompt copied to clipboard.", ) + def _copy_translation_qa_prompt(self): + """Copy the post-export RPG Maker QA skill with this game's paths.""" + try: + game_root = self.folder_edit.text().strip() + if not game_root or not Path(game_root).is_dir(): + self._log("⚠ Select and detect a game folder in Step 0 first.") + return + game_data = self._data_path + if not game_data or not Path(game_data).is_dir(): + self._log("⚠ No game data folder detected. Complete Step 0 first.") + return + replacements = { + "{{GAME_DATA_FOLDER}}": str(Path(game_data).expanduser().resolve()), + "{{GAME_ROOT}}": str(Path(game_root).expanduser().resolve()), + "{{VOCAB_FILE}}": str((Path(game_root) / "vocab.txt").expanduser().resolve()), + } + prompt = load_clipboard_skill("rpgmaker_translation_qa.md") + missing = [token for token in replacements if token not in prompt] + if missing: + raise ValueError( + "Translation QA skill is missing required placeholder(s): " + + ", ".join(missing) + ) + for token, value in replacements.items(): + prompt = prompt.replace(token, value) + QApplication.clipboard().setText(prompt) + self._log(f"RPG Maker game-data QA skill copied for {game_data}.") + except Exception as exc: + self._log(f"❌ Could not copy translation QA skill: {exc}") + def _copy_clipboard_skill(self, filename: str, success_message: str): try: prompt = load_clipboard_skill(filename) diff --git a/tests/test_bundled_updates.py b/tests/test_bundled_updates.py index f7231ae..615fa71 100644 --- a/tests/test_bundled_updates.py +++ b/tests/test_bundled_updates.py @@ -26,6 +26,7 @@ _SHIPPED_DATA_FILES = ( "data/skills/wrap_config.md", "data/skills/plugin_translation.md", "data/skills/ace_script_translation.md", + "data/skills/rpgmaker_translation_qa.md", "data/skills/image_translation.md", "data/skills/risky_codes.md", "data/skills/wolf_speakers.md", diff --git a/tests/test_workflow_prompts.py b/tests/test_workflow_prompts.py index 0463796..ee8d547 100644 --- a/tests/test_workflow_prompts.py +++ b/tests/test_workflow_prompts.py @@ -35,11 +35,28 @@ class WorkflowTranslationPromptTests(unittest.TestCase): self.assertIn("which listed scripts should you translate", prompt) self.assertIn("Ruby interpolation (#{...})", prompt) + def test_rpgmaker_qa_prompt_scales_and_requires_approval(self): + prompt = load_clipboard_skill("rpgmaker_translation_qa.md") + lowered = prompt.casefold() + self.assertIn("mechanically check 100%", lowered) + self.assertIn("deterministic stratified sample", lowered) + self.assertIn("control-code scope and placement", 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) + for placeholder in ( + "{{GAME_DATA_FOLDER}}", + "{{GAME_ROOT}}", + "{{VOCAB_FILE}}", + ): + self.assertIn(placeholder, prompt) + def test_static_clipboard_prompts_live_under_data_skills(self): expected = ( "wrap_config.md", "plugin_translation.md", "ace_script_translation.md", + "rpgmaker_translation_qa.md", "image_translation.md", "risky_codes.md", "wolf_speakers.md", @@ -58,6 +75,8 @@ class WorkflowTranslationPromptTests(unittest.TestCase): ): self.assertNotIn(constant, workflow_source) self.assertNotIn("_WOLF_SPEAKER_PROMPT", wolf_source) + self.assertIn("QA Game Data Skill", workflow_source) + self.assertIn('load_clipboard_skill("rpgmaker_translation_qa.md")', workflow_source) def test_image_translation_prompt_ends_skips_with_recovery_options(self): prompt = load_clipboard_skill("image_translation.md")