This commit is contained in:
DazedAnon 2026-07-09 14:23:23 -05:00
parent 47b0936a75
commit fb628f5f85
2 changed files with 74 additions and 102 deletions

View file

@ -330,8 +330,8 @@ Open the **Workflow** tab and choose **Wolf RPG (WolfDawn)** from the engine sel
| **3 Names** | Translate `names.json` (item/skill/enemy/map value names). WolfDawn tags each name with a per-entry **safety** badge (`safe`, `refs`, or `verify`). Phase 0 translates only `safe` entries; `refs` and `verify` names stay Japanese so inject skips them. Review the category breakdown for this game, pick **Translation mode** (Normal or Batch), and run **Translate safe names (Phase 0)**. |
| **4 Database** | Review the **discovery summary** to see where this game's text lives (standard RPG sheets vs custom dialogue tables). Database sheets are classified as foundation (items, skills, descriptions — translate first) or narrative (custom event/profile sheets — translate after foundation). Use **Translate foundation DB** then **Translate narrative DB**, or tick specific sheets and run **Translate checked sheets only**. Optional: copy the **DB structure prompt** for an AI audit and import the returned JSON into `wolf_json/db_profile.json`. |
| **5 Maps/Events** | Translate map scripts (`.mps`), common events (`CommonEvent.dat`), `Game.dat`, and Evtext. Run after Steps 34 so vocab and database terms are consistent. Configure speaker handling for low-confidence nameplate guesses. Batch mode is recommended for large `CommonEvent.dat` files. |
| **6 Precheck** | Dry-run selected JSON for safety-guard skips; fix those lines before writing binaries. |
| **7 Inject** | Always **Inject all** translated JSON from `translated/` into Data/ in one pass (keeps `names.json` and DB/map files in sync). Each file is reported individually; failures open a dialog with details. Uses pristine originals from `wolf_json/originals/`, then preserves name-only fields after names-inject. Optional **Layout-restore** re-applies source whitespace pads. |
| **6 Precheck** | Runs name reconcile + consistency check, then dry-runs selected JSON for safety-guard skips; fix those lines before writing binaries. |
| **7 Inject** | Always **Inject all** translated JSON from `translated/` into Data/ in one pass (keeps `names.json` and DB/map files in sync). Optional **Convert Japanese punctuation to ASCII**. Font-size drift from wrap is allowed automatically. Optional **Layout-restore** re-applies source whitespace pads. |
| **8 Package** | Run from loose `Data/` (backs up `Data.wolf``.bak`) or repack `Data.wolf` so you can playtest and see overflow in-game. Optionally rewrite existing `.sav` files so old Japanese saves load in the translated build. |
| **9 Fix wrap** | After packaging, paste overflowing in-game text to **search** `translated/` JSON and jump to the database sheet and row (sheet names match Step 4 and `names.json` notes, e.g. `├■街の噂MOB`). Edit the line, set wrap width, **Wrap this row** or **Wrap all overflowing rows in this sheet**, save, then **Inject all** (Step 7) and re-package to verify. Per-sheet widths are remembered in `wolf_json/wrap_profile.json`. **Advanced:** **wolf relayout** for event message boxes (maps/CommonEvent only — not bulletin-style DB UI) and **wolf desc-relayout** for standard 説明 description fields. |

View file

@ -15,8 +15,8 @@ Mirrors the RPGMaker WorkflowTab, driven by the vendored WolfDawn ``wolf`` CLI
(items, skills, descriptions) before narrative custom sheets;
harvests short label fields (map names, titles) into vocab.txt
Step 5 Maps/Events - .mps maps, CommonEvent, Game.dat, Evtext; speaker handling
Step 6 Precheck - dry-run selected JSON for safety-guard skips; edit those
lines before writing binaries
Step 6 Precheck - name consistency + reconcile, then dry-run inject for
safety-guard skips; edit those lines before writing binaries
Step 7 Inject - always inject every translated JSON into Data/ and
refresh wolf_json/ (full pass avoids name/DB wipe bugs);
optional layout-restore re-applies source whitespace pads
@ -2436,9 +2436,10 @@ class WolfWorkflowTab(QWidget):
def _build_step6_precheck(self, layout: QVBoxLayout):
layout.addWidget(_make_section_label("Step 6 · Inject Precheck"))
layout.addWidget(self._desc(
"Dry-run selected translated JSON before writing binaries. Lists only real "
"safety skips: control-code mismatch, or text not Shift-JIS encodable. "
"Fix those lines here, then continue to Step 7 (Inject)."
"Before inject: reconcile names.json → extract glossary spellings, "
"report name inconsistencies, then dry-run the selected JSON for real "
"safety skips (control-code mismatch, or text not Shift-JIS encodable). "
"Fix safety rows here, then continue to Step 7 (Inject)."
))
layout.addWidget(self._subheading("Translated files"))
@ -2467,8 +2468,15 @@ class WolfWorkflowTab(QWidget):
precheck_row = QHBoxLayout()
precheck_btn = self._register(_make_btn("Precheck selected", "#007acc"))
precheck_btn.setToolTip(
"Reconcile names → glossary, run names-check, then dry-run inject "
"safety guards on the ticked files."
)
precheck_btn.clicked.connect(self._precheck_inject_selected)
precheck_all_btn = self._register(_make_btn("Precheck all", "#007acc"))
precheck_all_btn.setToolTip(
"Same as Precheck selected, for every injectable file in translated/."
)
precheck_all_btn.clicked.connect(self._precheck_inject_all)
precheck_row.addWidget(precheck_btn)
precheck_row.addWidget(precheck_all_btn)
@ -2533,37 +2541,15 @@ class WolfWorkflowTab(QWidget):
self.en_punct_cb = QCheckBox("Convert Japanese punctuation to ASCII (--en-punct)")
self.en_punct_cb.setChecked(self._setting("en_punct", "true") == "true")
self.en_punct_cb.setToolTip(
"Fold fullwidth JP punctuation to ASCII on inject. "
"Inline-code drift for \\f shrink is applied automatically."
)
self.en_punct_cb.stateChanged.connect(
lambda: self._save_setting("en_punct", "true" if self.en_punct_cb.isChecked() else "false")
)
layout.addWidget(self.en_punct_cb)
self.drift_cb = QCheckBox(
"Allow inline-code drift (--allow-code-drift; auto for names-wrap \\f shrink)"
)
self.drift_cb.setChecked(self._setting("allow_code_drift", "false") == "true")
self.drift_cb.stateChanged.connect(
lambda: self._save_setting(
"allow_code_drift", "true" if self.drift_cb.isChecked() else "false"
)
)
layout.addWidget(self.drift_cb)
check_row = QHBoxLayout()
check_btn = self._register(_make_btn("Check name consistency", "#3a3a3a"))
check_btn.clicked.connect(self._names_check)
reconcile_btn = self._register(_make_btn("Reconcile names → glossary", "#007acc"))
reconcile_btn.setToolTip(
"Rewrite extract lines so each names.json source uses one English value. "
"Translated names.json entries win; if the glossary is still Japanese "
"(refs/verify), divergent extracts are unified to the majority spelling."
)
reconcile_btn.clicked.connect(self._names_reconcile)
check_row.addWidget(check_btn)
check_row.addWidget(reconcile_btn)
check_row.addStretch()
layout.addLayout(check_row)
layout.addWidget(_make_hr())
layout.addWidget(self._subheading("Files that will be injected"))
@ -3605,7 +3591,6 @@ class WolfWorkflowTab(QWidget):
return
manifest = self._read_manifest()
en_punct = self._inject_flag_en_punct()
allow_drift = self._inject_flag_allow_drift()
game_json_dir = self._work_dir()
def task(log, progress=None):
@ -3618,6 +3603,7 @@ class WolfWorkflowTab(QWidget):
self._ensure_originals(manifest, log, progress, quiet=True)
# allow_code_drift=False: inject still auto-passes it for \\f shrink.
report = wolf_inject.inject_selected(
sorted(selected),
manifest_entries=entries,
@ -3625,7 +3611,7 @@ class WolfWorkflowTab(QWidget):
originals_dir=originals_dir,
translated_dir=self._tool_root() / "translated",
game_root=game_root,
allow_code_drift=allow_drift,
allow_code_drift=False,
en_punct=en_punct,
log_fn=log,
)
@ -3830,37 +3816,75 @@ class WolfWorkflowTab(QWidget):
return self.en_punct_cb.isChecked()
return self._setting("en_punct", "true") == "true"
def _inject_flag_allow_drift(self) -> bool:
if hasattr(self, "drift_cb"):
return self.drift_cb.isChecked()
return self._setting("allow_code_drift", "false") == "true"
def _run_inject_precheck(self, selected: set[str]):
manifest = self._read_manifest()
en_punct = self._inject_flag_en_punct()
allow_drift = self._inject_flag_allow_drift()
state: dict = {}
def task(log, progress=None):
from util import wolfdawn
from util.wolfdawn import inject_precheck as wolf_pre
from util.wolfdawn import names as wolf_names
translated = self._tool_root() / "translated"
name_notes: list[str] = []
# Name hygiene first (same work the old Step 7 buttons did).
if (translated / "names.json").is_file():
report_names = wolf_names.reconcile_translated_dir(
translated, dry_run=False
)
reconcile_msg = wolf_names.format_reconcile_summary(report_names)
log(reconcile_msg)
for change in report_names.changes[:40]:
log(
f" {change.json_file}: {change.source!r} "
f"{change.old_text!r}{change.new_text!r} ({change.reason})"
)
if len(report_names.changes) > 40:
log(f" … and {len(report_names.changes) - 40} more")
name_notes.append(reconcile_msg)
else:
log("Name reconcile skipped (no translated/names.json).")
json_files = []
for entry in manifest["entries"]:
p = self._translated_or_source(entry["json"])
if p:
json_files.append(str(p))
if json_files:
check = wolfdawn.names_check(json_files, log_fn=log)
if check.returncode == 0:
name_notes.append("Name usage is consistent across files.")
else:
name_notes.append(
"names-check reported inconsistencies (see log)."
)
else:
name_notes.append("names-check skipped (no JSON found).")
entries = manifest["entries"]
data_dir_path = Path(manifest["data_dir"])
game_root = Path(manifest.get("root") or self._game_root)
originals_dir = game_root / WORK_DIR_NAME / "originals"
self._ensure_originals(manifest, log, progress, quiet=True)
# Font-size drift from wrap/names-wrap is auto-allowed inside precheck.
report = wolf_pre.precheck_selected(
sorted(selected),
manifest_entries=entries,
data_dir=data_dir_path,
originals_dir=originals_dir,
translated_dir=self._tool_root() / "translated",
allow_code_drift=allow_drift,
translated_dir=translated,
allow_code_drift=False,
en_punct=en_punct,
log_fn=log,
)
state["report"] = report
return True, wolf_pre.format_precheck_summary(report)
state["name_notes"] = name_notes
summary = wolf_pre.format_precheck_summary(report)
if name_notes:
summary = " · ".join(name_notes) + "\n" + summary
return True, summary
def _after(ok: bool, msg: str):
from util.wolfdawn import inject_precheck as wolf_pre
@ -3870,12 +3894,16 @@ class WolfWorkflowTab(QWidget):
self._inject_precheck_label.setText(msg or "Precheck failed.")
return
self._populate_inject_precheck(report)
self._inject_precheck_label.setText(wolf_pre.format_precheck_summary(report))
label = wolf_pre.format_precheck_summary(report)
notes = state.get("name_notes") or []
if notes:
label = " · ".join(notes) + "\n" + label
self._inject_precheck_label.setText(label)
if report.safety_issues:
QMessageBox.warning(
self,
"Inject precheck",
wolf_pre.format_precheck_summary(report)
(msg or label)
+ "\n\nSelect a safety row below, fix the text, Save line, then re-run precheck.",
)
else:
@ -3996,62 +4024,6 @@ class WolfWorkflowTab(QWidget):
self._run_task(task, on_done=_after)
def _names_check(self):
if not self._require_manifest():
return
manifest = self._read_manifest()
def task(log, progress=None):
from util import wolfdawn
json_files = []
for entry in manifest["entries"]:
p = self._translated_or_source(entry["json"])
if p:
json_files.append(str(p))
if not json_files:
return False, "No translated JSON found. Run Steps 4-5 first."
res = wolfdawn.names_check(json_files, log_fn=log)
if res.returncode == 0:
return True, "Name usage is consistent across files."
return True, "names-check reported inconsistencies (see log above)."
self._run_task(task)
def _names_reconcile(self):
"""Force extract JSON to one English per glossary name (names.json wins)."""
translated = self._tool_root() / "translated"
if not (translated / "names.json").is_file():
QMessageBox.warning(
self,
"Reconcile names",
"translated/names.json not found. Finish Step 3 first.",
)
return
def task(log, progress=None):
from util.wolfdawn import names as wolf_names
report = wolf_names.reconcile_translated_dir(translated, dry_run=False)
summary = wolf_names.format_reconcile_summary(report)
log(summary)
for change in report.changes[:40]:
log(
f" {change.json_file}: {change.source!r} "
f"{change.old_text!r}{change.new_text!r} ({change.reason})"
)
if len(report.changes) > 40:
log(f" … and {len(report.changes) - 40} more")
return True, summary
def _after(ok: bool, msg: str):
if ok:
QMessageBox.information(self, "Reconcile names", msg)
else:
QMessageBox.warning(self, "Reconcile names", msg or "Reconcile failed.")
self._run_task(task, on_done=_after)
# ── Step 8: Package (+ optional saves) ─────────────────────────────────────
def _build_step8_package(self, layout: QVBoxLayout):