wikicommit-translate
Translate wiki pages into the target languages configured in .wikicommit/config.yml, either one named page or every untranslated or stale page, writing the results locally with no Git operations. Use
SKILL.md.claude/skills/wikicommit-translate/SKILL.md
GitHubwikicommit-translate
Interactive translation Skill. Per-page processing is identical to the unattended Phase 4 pipeline — inject the source page's full text plus DefinedTerm/ glossary terms and the source→target term table built from them, generate a translation, check it against the source page with a review subagent (recorded under .wikicommit/review/ as translate-check), and attach translated_from / source_commit / translated_at / translated_by / translated_with. The only difference is who calls it and when: this Skill is invoked by a human, writes locally, and performs no Git operations. Run /wikicommit-merge afterward to commit and open a PR.
Usage
/wikicommit-translate <page> [--lang <target>] # translate a single page
/wikicommit-translate # batch mode: process all untranslated/stale (page, target) pairs
<page> must be a source page (a page without translated_from) — e.g. .wikicommit/entity/ja/Person/yamada-taro.md. If the given page itself has translated_from (i.e. it is already a translation), stop and tell the user to run this on the original page instead.
Processing Flow — the workflow engine holds the order
The order of this Skill's steps is kept by .wikicommit/scripts/skill_workflow.py, not by you. It returns one step at a time, checks on disk that the step was really done before moving on, and keeps its place in this run's record — so after a compaction or a new session you pick up exactly where the run stopped instead of re-deciding which pairs are already translated, reviewed and written back.
Start the run, passing each argument this Skill was invoked with as its own --arg (add --non-interactive when no person can answer questions in this run — a subagent, a scheduled or unattended run; the workflow engine then answers the five-pair question itself with "first five"):
python .wikicommit/scripts/skill_workflow.py start --workflow workflow.yaml \
--model "<the model ID this runtime reports for you>" --arg="<each argument, one --arg each>"
Write each argument with = — --arg=<page> --arg=--lang --arg=<target> — because a value starting with -- given after a space is read as an option of its own and the run does not start. workflow.yaml is in this Skill's directory — the one holding this SKILL.md, which the runtime names when it loads the Skill — not in the repository root, because the Skills may be installed under .claude/skills/ or .agents/skills/. Commands still run from the repository root, so spell the path out from there.
Then loop:
Read the JSON it printed. Keep the
runpath — every later call names it.If
notesis present, report every line of it to the user.If
stepisnull, the run is over —finished(sayfinished_becauseif present, e.g. "Nothing to translate"), or halted (sayhalted_reason). Stop.The first step is a check the workflow engine runs itself, at the start of the run, before anything is translated: if
.wikicommit/config.ymldoes not exist it halts and you tell the user to run/wikicommit-initfirst; if.wikicommit/review-rules.mddoes not exist it halts and you tell the user to run/wikicommit-init --no-overwrite. Every page this run writes goes through thetranslate-checkreview, and that review follows those rules; without them it degrades to a bare re-read while still writing a record that a review happened, and finding out at the first review would throw that translation away. The same check halts when the named page is itself a translation (hastranslated_from— tell the user to run this on the original page instead) and when there is no target language ("No target language. Pass--lang <lang>, or settargetsinconfig.yml.", fixed English — it is read by the operator, not by readers of the wiki). Without a page, it also halts when--langnames a language not intargetsthat has no translations yet:check_translation_status.pyreportsUNTRANSLATEDonly for the configured targets (andSTALEonly for translations that exist), so batch mode could never find anything — tell the user to name a page or add the language totargets.If
blockedis true, the completion check for this step has failed repeatedly: stop and report the lastreasonto the user. Do not work around a check.A
humanstep: ask the userask, offeringchoices, and pass the answer back with thethencommand.An
agentstep: read the file named ininstructionsnow and follow it. When it is done, run thethencommand with one of theoutcomes,--tokenset to thepass_tokenin the instructions file's frontmatter, and the--reason,--pageor--countthat step's instructions ask for. If the response haswhen_nobody_can_answer, that is the rule for any question the step would ask.doneprints the next step in the same form. If it prints"accepted": false, the step is not complete: readreason, fix what it names, and rundoneagain. It never skips ahead, and it refuses a step other than the current one.
skill_workflow.py next <run> prints the current step again without changing anything — use it whenever you are unsure where the run is, and always after a compaction or when resuming in a new session (skill_workflow.py status lists the runs still open). The pairs and their order are in the run record's workflow.lists, and each finished pair is in its workflow.log; read them from there, not from memory. A run whose session is gone for good is closed with skill_workflow.py abandon <run> --reason "<why>", which only a person should decide.
Why it works this way. Each pair ends in three writes — the translation, its source_commit write-back and its review record — and a long batch done from memory is exactly where the last of them goes missing. The workflow engine checks all three on disk before it moves to the next pair, and /wikicommit-merge refuses a change carrying files that a still-open run touched.
The steps (the table is here as well as in the workflow engine's answers so the pointers survive a compaction):
| Step | Kind | Read | What it does |
|---|---|---|---|
preflight |
script | — | the checks above |
collect |
script | — | lists the (source page, target language) pairs, each written <source page> -> <lang>; none → the run finishes with "Nothing to translate" |
batch-cap |
human | — | batch mode only, when there are more than 5 pairs; a non-interactive run takes the first 5 |
select |
script | — | applies that answer |
translate (per pair) |
agent | references/translate-page.md |
Step 4: translate, review (translate-check), record, write |
rebuild-index |
script | — | rebuild_index.py, once for the whole run |
report |
agent | references/completion-report.md |
close the run and report, listing every discarded pair |
Which pairs collect lists. With a page argument (single-page mode), one pair per target: --lang <target> if given, otherwise every translation.targets entry, skipping the page's own language. Without one (batch mode), every UNTRANSLATED and STALE pair check_translation_status.py reports — for a STALE line the pair is the translation's translated_from and lang, and with --lang <target> only that language's pairs — with every DefinedTerm pair first, then ascending by the reported path. Translating prose before its glossary is settled is backwards: DefinedTerm is the most-referenced type in a wiki, and plain path order would put it wherever DefinedTerm/ happens to sort, often after the pages that cite it. Step 4's term table is re-read from disk for each pair, so later pairs see the target-language terms this run has already settled — and because the list is DefinedTerm-first, answering "first five" settles the glossary within this run instead of deferring it behind unrelated pages. A non-interactive run never reads the absence of an answer as "all": the deferred pairs change nothing on disk, so the next run reports them again.
Single-page mode has no work list to reorder, so it cannot settle the glossary first — it uses whatever target-language DefinedTerm pages already exist. Translating a wiki's DefinedTerm pages before its other pages is therefore worth doing by hand here, or by running batch mode instead.
If .wikicommit/scripts/skill_workflow.py or _workflow_checks.py does not exist (.wikicommit/scripts/ older than this Skill), the run cannot start (the first step halts on the latter): tell the user to run /wikicommit-update first.
Notes
- Do not commit or create a PR against
mainor any branch (that iswikicommit-merge's responsibility) - Do not write to
.wikicommit/schema/(read-only) - No Git operations of any kind — this Skill only reads the working tree (including via
git logforsource_commitandgit hash-objectfor the rules file) and writes new/updated files under.wikicommit/entity/and new review records under.wikicommit/review/