Install
$ agentstack add skill-roshaw-claude-translation-agency-translate-audit ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
translate-audit
A read-only audit for a multi-language project, in two modes:
- Coverage (default) — is anything missing? A cheap, deterministic set comparison across every
language. No agents, no LLM cost. Answers, per language: what is missing, where it is present (which other languages carry it, and the value — so you can see what it should be), and how to fix it.
- Quality (
--deep) — is what's there any good? Spawns thetranslate-leadto run its C1–C7
review over the existing translations and report polish opportunities (weak terminology, machine-translation stiffness, register/parity drift) plus correctness bugs, each with a severity and a suggested rewrite. This mode costs tokens (it reads and LLM-reviews content), so it is scoped/ sampled by default — see "Deep mode" below.
It never edits a file itself — both modes only read and report (deep mode's --fix hands the flagged files to /translate, which does the writing). Fixing is always /translate's job; this skill tells you exactly what to run.
> Why a separate skill from /translate? /translate has a completeness gate, but it only > checks the languages in its run scope and only against the source. This audit is broader and > symmetric: it compares every language against the union of all languages, so it also > catches keys that exist in Bulgarian or German but are missing from the source (English) — a > class /translate structurally can't surface, because it never adds keys to or edits the source.
What this skill does NOT do
- Translate or edit anything. No file is written except the report(s). To close gaps or apply
polish, run /translate (this skill prints the exact command; --deep --fix invokes it for you).
- Add keys to the source, or "fix" a source gap. It flags keys missing from the source (with
the value other languages use) and leaves the decision to you — add it to the source, or delete the orphan from the other languages.
- Judge quality in the default (coverage) mode. Coverage checks presence/completeness only.
Quality judgment happens only in --deep, and even then the skill reports; it does not rewrite.
- Commit or push.
Invocation
/translate-audit # COVERAGE audit, configured/current project, all languages (cheap)
/translate-audit --path # point at a project, e.g. --path C:\Projects\MyApp
/translate-audit # shorthand for --path
/translate-audit --langs # restrict to these languages (default: all present)
/translate-audit --format md|text # report format (default md, written to a file + summarized in chat)
/translate-audit --deep # + QUALITY review of existing translations (spawns the Lead; costs tokens)
/translate-audit --deep --sample # deep-review N high-signal strings per language (default 40)
/translate-audit --deep --files # deep-review exactly this set instead of a sample
/translate-audit --deep --domain # domain lens for the quality review (default: config, else general)
/translate-audit --deep --fix # after the report, hand the flagged files to /translate to apply
Flags compose: --deep --langs de,fr --sample 60. --sample, --fix, and --domain apply to --deep only.
Bash discipline (HARD RULES)
Same as the rest of the toolkit: each command is its own Bash call (no &&/;/|), no shell loops/branches or awk/jq/sed for logic, and file discovery/counts use Glob/Grep, never ls | grep / find | head / grep | wc. Build the key inventory by Read-ing each locale file and comparing the sets in your context — not with a shell script.
Step 0 — Resolve the project and its languages
- Project root — resolve
--path/ bare `` → that folder; else the current folder if it
has a translation.config.json; else offer the registry picker (read the toolkit's projects/registry.json — treat a missing file as empty), same as /translate Step 0. Read the root's translation.config.json if present (for sourceLang, include/exclude, wordpress).
- Source language — from
config.sourceLang, else infer from the layout (en.json,-en.po,
en/, .en.md). The source is one language in the matrix, but it is treated specially (see the source-gap class).
- All present languages — detect every language the project already has (not just configured
targets): the locale codes appearing in catalog filenames, .po locales, and content-tree folders/suffixes. This full set (source included) is the audit's universe. --langs narrows it.
- Formats present — same detection table as
/translateStep 1 (message catalogs, gettext,
content trees). Record which formats to inventory.
Step 1 — Build the per-language inventory
For each format, for each language, collect the set of translatable units and their values. Use Glob to enumerate files and Read to load them; hold the sets in context.
- Message catalogs (
.json/.ts/.js/.arb/.yaml/.resx/.strings): the unit is the
flattened dotted key path (nav.home, errors.required). Record, per language: the key set and each key's value. Keep array indices in the path (steps.0.title) so array-length gaps show up.
- gettext (
.po/.pot): the unit is themsgid(plusmsgctxtif present). Record, per
language: the msgid set and whether each msgstr (and every plural msgstr[n]) is filled or empty. A .pot template, if present, is the authoritative key universe for gettext.
- Content trees (
content//**,*..md, mirrored MDX/HTML): the unit is the
logical page (the source-relative path with the language stripped). Record, per language: which pages exist, and each page's source_hash (if the format stamps one).
Step 2 — Compute the reference universe and classify each gap
The reference universe per format is the union of units across all audited languages — so a key present in any language counts as "expected." For each unit × each language, classify:
OK— present and non-empty (and, for a target, not a leftover). Not reported.MISSING_IN_TARGET— absent in this (non-source) language but present in the source. → the
normal gap; /translate fills it.
MISSING_IN_SOURCE— absent in the source language but present in ≥1 translation. This is
the inversion: e.g. a key in bg/de that en doesn't have. /translate cannot fix it (it never edits or adds source keys). Flag it for a human decision and show the value(s) the other languages carry.
EMPTY— the key/msgstr exists but the value is empty/whitespace. →/translatefills it.LEFTOVER— a target value byte-identical to the source value and containing ≥3
source-language letters, i.e. never translated. (Skip the legitimately-shared exempt classes: brands, numbers, code, URLs, citations, and anything a project glossary lang=* row or config.doNotTranslate rule covers — a match there is correct, not a leftover.) → /translate fixes it.
ORPHAN— a key present in only one language (and not the source). Often a stale/renamed
key or a not-yet-propagated addition. Report it under the owning language so you can decide keep vs delete.
For content trees, add:
MISSING_FILE— a source page with no counterpart file in a language. →/translate(or
/translate-add-locale if the whole language is new).
STALE— the counterpart exists but itssource_hash≠sha256(current source)(compute the
hash with one plain sha256sum Bash call per file). → /translate re-translates it.
Step 3 — Assemble the report
Write the report (default a Markdown file translation-coverage-.md at the project root; also print a tight summary to chat). Structure:
- Header — project, source language, languages audited, formats, date.
- Coverage matrix — one row per language: units present / total-in-universe, percentage, and gap
counts by class. Sort worst-covered first. Example:
`` Language Coverage Missing Empty Leftover Orphan en (src) 612 / 640 — — — — ⚠ 28 keys exist only in translations (source gaps) de 628 / 640 12 0 0 3 bg 640 / 640 0 0 2 0 fr 590 / 640 50 6 0 0 ``
- Per-language detail — for each language, the missing/empty/leftover/orphan units, each with:
key/path, the gap class, present-in [langs], and (for MISSING_IN_SOURCE/orphans) the value the other language carries. Group by file so a fix maps to one place.
- Source gaps (needs your decision) — the
MISSING_IN_SOURCEset called out separately, since
/translate won't touch it. For each: the key, which languages have it, and their value(s), with the two options (add to source, or delete the orphan from the other languages).
- Fix plan — the concrete commands, e.g.:
- Fill the normal gaps in every language:
/translate --full - Fill just one language:
/translate --full --to fr - Fix only specific files:
/translate --files src/i18n/fr.json,content/fr/** - Content-tree staleness:
/translate(it re-translates stalesource_hashpages automatically). - Source gaps: no command — add the key to `
(value shown) then/translate`, or
remove the orphan key from the languages listed.
Step 4 — Summarize in chat + point to the fix
Print: the coverage matrix, the total gap counts by class, the count of source gaps (highlighted, since they need a human), and the top fix command. End with the report file path. Do not run /translate yourself — offer it as the next step.
If every language is at 100% with no empties, leftovers, orphans, or source gaps, say so plainly: ✅ All N languages complete — no coverage gaps found.
Step 5 — Deep mode (--deep): quality review of existing translations
Runs only when --deep is passed. The coverage pass (Steps 1–4) always runs first — gaps are context for the quality read, and there's no point deep-reviewing a string that's missing. This mode spawns the translate-lead to judge the translations that do exist, so it costs tokens and is deliberately scoped.
5a — Pick the scope (sampled by default). Deep review sends real content through an LLM reviewer, so never silently deep-review a whole large project:
--files→ review exactly that set.- else
--sample(default 40) → per audited language, select N high-signal units — the
substantive ones (domain prose, message values with real sentences, headings, marketing/legal/ financial copy, longer .po entries), not pure chrome (nav/buttons) or identity tokens. Prefer units that differ across languages (more likely to carry quality variance). Spread across files.
--langsnarrows which languages are reviewed (default: all non-source present languages).- Always state the sample in the report ("deep-reviewed 40 / 612 units in
de") — no silent
truncation. If the user wants everything, tell them the rough size and let them pass --files '**'.
5b — Load the domain lens (so C2/C6 aren't generic): resolve the specialization (--domain, else config.specialization, else general) → specializations/.md; the config.glossary / projects//glossary.csv; and config.context. A quality review without these is just dictionary opinion.
5c — Spawn the Lead in review-only (audit) mode. One Agent({ subagent_type: "translate-lead" }) with a brief that sets mode: audit and lists the scoped review_items (each: sourcefile, targetfile, target_lang, and the keys/sections in scope), plus specialization_path, glossary_path, context, and do_not_translate. In audit mode the Lead does not spawn workers and does not translate — it treats each existing target value as the candidate, runs C1–C7 against the source, and returns findings with a severity:
- must-fix — correctness: C1 leftover, C3 identity-token drift, C4 dropped/renamed placeholder,
C5 broken markup.
- polish — quality: C2 a better term of art exists, C6 machine-translation stiffness / wrong
register / unnatural phrasing, C7 parallel-copy drift. Each finding carries: file · key/line · category · severity · current value · suggested rewrite · confidence. The Lead applies nothing (read-only), and its per-batch cycle cap / cost rules apply.
5d — Write the quality report translation-quality-.md (separate from the coverage report), and summarize in chat. Structure: header (scope actually reviewed, sample size, domain, glossary used); a per-language quality summary (must-fix count / polish count); then per-language, per-file findings, must-fix first, then polish — each with current value → suggested rewrite and the one-line reason. End with the fix plan.
5e — Fix handoff.
- Default (no
--fix): print the command to apply — `/translate --files --domain
` — and stop. The panel's own C1–C7 pass will apply the improvements and re-review.
--fix: invoke/translateon the flagged files, passing the quality report path so the panel
uses the findings as a targeted correction list (it still re-reviews and owns the write + verify + commit decision). Deep-audit never edits files directly — /translate does.
If deep review finds no must-fix and no polish items in scope, say so: ✅ Deep review (N units, M languages): no quality issues found in scope.
Notes
- This skill never edits files. Coverage is pure read. Deep mode reads + LLM-reviews and writes
only its report; even --deep --fix doesn't edit here — it invokes /translate, which owns the write, verify, and commit decision. "Just fix it too" = the --fix handoff, not an inline edit.
- Deep mode costs tokens; coverage doesn't. Default coverage is free/deterministic. Reach for
--deep when you want a quality read, and keep it scoped (--sample/--files/--langs).
- The source is a language in the matrix, but special. Coverage % for the source is informational;
the meaningful source finding is the MISSING_IN_SOURCE set. Never propose auto-editing the source.
- Report files are disposable.
translation-coverage-.mdand (deep)translation-quality-.md
are snapshots; add them to the project's ignore file if the user doesn't want them tracked (offer, don't assume).
- Large projects: enumerate with
Globper format and read locale files once each; compare sets
in context. Don't re-read the same file per language.
Reference
- Fix the gaps / apply polish:
/translate(.claude/skills/translate/SKILL.md) — fills
missing/empty/leftover/stale and re-reviews quality.
- Deep-mode reviewer:
.claude/agents/translate-lead.md— run inmode: audit(review-only, C1–C7
over existing translations, no worker spawns, no edits).
- Add a whole new language first:
/translate-add-locale. - Config (source lang, scope, specialization, glossary, doNotTranslate):
translation.config.json. - Registry / per-project memory:
projects/registry.json,projects//notes.md.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: roshaw
- Source: roshaw/claude-translation-agency
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.