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