feat(rpgmaker): add post-export translation QA workflow
This commit is contained in:
parent
289699d6cd
commit
2ddb47dce1
5 changed files with 233 additions and 2 deletions
159
data/skills/rpgmaker_translation_qa.md
Normal file
159
data/skills/rpgmaker_translation_qa.md
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
# QA Exported RPG Maker Translations
|
||||
|
||||
<task_context>
|
||||
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.
|
||||
</task_context>
|
||||
|
||||
## 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.
|
||||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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] = {
|
|||
"<b>Export</b> - copy finished translations from <code>translated/</code> back "
|
||||
"into the game's data folder:<br>"
|
||||
"• <b>Export Active Files</b> - only names currently in <code>files/</code><br>"
|
||||
"• <b>Export ALL</b> - everything under <code>translated/</code><br><br>"
|
||||
"• <b>Export ALL</b> - everything under <code>translated/</code><br>"
|
||||
"• Then run <b>QA Game Data</b> against the exported files. It compares every preserved "
|
||||
"<code>_original</code> source with its translation, reports prioritized findings, and "
|
||||
"waits for approval before editing the game data<br><br>"
|
||||
"<b>Create Public Release ZIP</b> 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)
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
Loading…
Reference in a new issue