|
| 1 | +--- |
| 2 | +name: translation |
| 3 | +description: Translate the Hugo docs site (content/en/**/*.md) into all supported languages using local Claude subagents. Incremental — skips files whose sourceHash is unchanged, and patches only changed paragraphs in articles. Use when the user asks to translate the docs, sync translations, or translate a specific page/language. Optional args narrow scope: a language code (cs,de,fr,es,sk,zh,pt,it,ja,ko), a path substring, and/or a type (md). |
| 4 | +--- |
| 5 | + |
| 6 | +# Docs translation (local Claude agent) |
| 7 | + |
| 8 | +Translating is done by **local Claude subagents** — no API key. It is incremental: |
| 9 | +unchanged files are skipped by hash, and changed articles are patched |
| 10 | +paragraph-by-paragraph to save time and tokens. |
| 11 | + |
| 12 | +Always run from the repository root: `c:\jenasoft\dbgate-docs`. |
| 13 | + |
| 14 | +This is a port of the same skill in `../web-dbgate-io`. Keep the two in sync where |
| 15 | +practical — `translate_plan.py` and `set_hash.py` are intentionally near-identical |
| 16 | +so fixes flow both ways. |
| 17 | + |
| 18 | +## Supported languages |
| 19 | +`cs` Czech · `de` German · `fr` French · `es` Spanish · `sk` Slovak · |
| 20 | +`zh` Simplified Chinese · `pt` Portuguese (Brazil) · `it` Italian · `ja` Japanese · `ko` Korean |
| 21 | + |
| 22 | +A language is only *published* once it has an entry in `hugo.toml`. Adding content |
| 23 | +under `content/<lang>/` without that entry does nothing; adding the entry without |
| 24 | +content publishes untranslated English duplicates under the prefix. Do both together. |
| 25 | + |
| 26 | +## Step 1 — Build the work plan |
| 27 | + |
| 28 | +Parse the user's args into optional filters and run the planner: |
| 29 | + |
| 30 | +``` |
| 31 | +python .claude/skills/translation/translate_plan.py [--lang cs,de] [--path working-with-data] [--type md] |
| 32 | +``` |
| 33 | + |
| 34 | +- A bare language code in the args → `--lang`. |
| 35 | +- Any other token (e.g. `working-with-data/`, a filename, a slug) → `--path`. |
| 36 | +- `md` → `--type`. |
| 37 | +- No args → full site (every language, every file) = 560 jobs. |
| 38 | + |
| 39 | +The planner prints JSON: `summary`, `deletions` (stale translation files to remove), |
| 40 | +and `jobs`. Each job has `type`, `mode`, `lang`, `lang_name`, `en_path`, `out_path`, |
| 41 | +`en_hash`, and `base_rev`. **Save the JSON to a file** (e.g. `Write` it to |
| 42 | +`.translation-plan.json`) so subagents can read individual jobs without you holding |
| 43 | +the whole list in context. |
| 44 | + |
| 45 | +## Step 2 — Confirm scope |
| 46 | + |
| 47 | +Report `summary.jobs` and the per-language / per-type breakdown. |
| 48 | + |
| 49 | +- If **jobs > 25 and the user gave no filter**, do NOT translate everything blindly — |
| 50 | + show the breakdown and ask whether to proceed with the full run or narrow scope |
| 51 | + (each job is a subagent call; a full run is a large token spend). |
| 52 | +- If scope is small or the user already specified a filter, proceed. |
| 53 | + |
| 54 | +## Step 3 — Deletions |
| 55 | + |
| 56 | +For every path in `deletions`, delete it (the English source was removed or marked |
| 57 | +`skipTranslation: true`). Use `rm`/`Remove-Item`. |
| 58 | + |
| 59 | +## Step 4 — Translate (one subagent per job) |
| 60 | + |
| 61 | +Spawn subagents with the **Agent** tool (subagent_type `general-purpose`), in parallel |
| 62 | +waves of up to ~8 at a time. Give each subagent its single job object and the rules |
| 63 | +below. The subagent **writes the output file and stamps the hash itself** — it must NOT |
| 64 | +return the translated content to you (that would waste tokens). Tell it to return only |
| 65 | +a one-line status. |
| 66 | + |
| 67 | +After writing/editing the output file, every subagent finishes by stamping the hash: |
| 68 | + |
| 69 | +``` |
| 70 | +python .claude/skills/translation/set_hash.py md <out_path> <en_path> <lang> |
| 71 | +``` |
| 72 | + |
| 73 | +(`set_hash.py` is idempotent — it removes any old hash markers first.) |
| 74 | + |
| 75 | +### Universal translation rules |
| 76 | +- Translate natural-language text only. Never translate or alter: |
| 77 | + - Hugo shortcodes `{{< … >}}`, `{{ … }}`, `{{% … %}}` — this site uses |
| 78 | + `{{< mdi "icon-name" >}}`, `{{< check >}}`, `{{< center "text" >}}`. The |
| 79 | + **argument of `center` is display text and IS translated**; `mdi` icon names |
| 80 | + are not. |
| 81 | + - anchor links `[text](#anchor)`, code blocks/spans, HTML tag names/attributes |
| 82 | + - placeholders, URLs of images (``, `<img src>`) — images are not |
| 83 | + language-specific and are served from a shared path |
| 84 | +- Preserve all Markdown/HTML structure, indentation, and quoting exactly. |
| 85 | +- Keep the same quote style (single vs double) as the source. |
| 86 | +- Keep DbGate product/edition names untranslated: DbGate, DbGate Cloud, DbGate Central, |
| 87 | + Community, Premium, Team Premium. Database and vendor names too (MySQL, PostgreSQL…). |
| 88 | + |
| 89 | +### md jobs (`content/en/…` → `content/<lang>/…`) |
| 90 | + |
| 91 | +**Front matter:** |
| 92 | +- Translate only `title` (and `subtitle`, which exists on one page). |
| 93 | +- Keep `weight`, `icon`, `edition`, `demo_url`, `layout` and any other field unchanged. |
| 94 | + `weight` drives nav order and must stay identical to English. |
| 95 | +- Keep YAML valid — quote any value containing a colon. |
| 96 | +- **`aliases:` — delete the entire `aliases:` block from translated files.** |
| 97 | + Aliases are legacy *English* URLs. Hugo does **not** language-prefix them (it only |
| 98 | + does so for `IsMultihost` sites, which this is not), so a verbatim copy silently |
| 99 | + hijacks the English redirect: the translated page takes over `/maps/`, the prefixed |
| 100 | + route is never created, and **the build still exits 0 with no warning**. Which |
| 101 | + language wins depends on build order, so it shifts as languages are added. |
| 102 | + Translations must never contain an `aliases:` key. See Step 5's guard. |
| 103 | + |
| 104 | +**Body links:** for internal **root-relative** links (`](/dbgate/…)`), prepend the |
| 105 | +language code → `](/cs/dbgate/…)`. Leave relative links (`](../dbgate-central/)`) |
| 106 | +alone — they already resolve correctly under the language prefix. Do not change |
| 107 | +image paths. |
| 108 | + |
| 109 | +Note `content/en/dbgate/customization/env-variables.md` links to `/features/administration`, |
| 110 | +which lives on the **web-dbgate-io** site, not here. Prefixing is still correct |
| 111 | +(`/cs/features/administration` exists there). Do not convert links to `relref` — it |
| 112 | +would fail the build on that cross-site link. |
| 113 | + |
| 114 | +**Modes:** |
| 115 | +- **mode `new`** — read `en_path`, translate the whole file per the rules, write to |
| 116 | + `out_path`, then `set_hash.py md <out_path> <en_path> <lang>`. |
| 117 | +- **mode `changed` with `base_rev`** — run `git diff <base_rev> -- <en_path>` to see |
| 118 | + exactly what changed in the English since the translation was last made. Read the |
| 119 | + existing `out_path`. Apply the *equivalent* translated changes with `Edit`, touching |
| 120 | + only the affected paragraphs/fields. Leave everything else verbatim. Then `set_hash.py`. |
| 121 | +- **mode `changed` without `base_rev`** — read `en_path` and the existing `out_path`. |
| 122 | + The translation is structurally parallel (same headings/order). Compare block by block; |
| 123 | + re-translate with `Edit` only the blocks whose translation no longer matches the English. |
| 124 | + Leave matching blocks untouched. Then `set_hash.py`. |
| 125 | + |
| 126 | +### toml / yml jobs |
| 127 | +This repo has no `data/` dir and no project-level `i18n/en.toml` — the relearn theme |
| 128 | +supplies all UI strings, and it ships translations for every supported language except |
| 129 | +**`sk`**. So the planner emits no toml/yml jobs today; those code paths are guarded and |
| 130 | +inert, kept only for parity with web-dbgate-io. |
| 131 | + |
| 132 | +If Slovak is rolled out, add a project-level `i18n/sk.toml` seeded from |
| 133 | +`themes/hugo-theme-relearn/i18n/en.toml` (~47 keys; Hugo merges project i18n over theme |
| 134 | +i18n). Once an `i18n/en.toml` exists, the toml job type reactivates on its own. |
| 135 | + |
| 136 | +## Step 5 — Verify, then report |
| 137 | + |
| 138 | +**The alias guard is mandatory after any md run** — it is the only thing standing in for |
| 139 | +the build error Hugo does not give you: |
| 140 | + |
| 141 | +```bash |
| 142 | +# MUST return nothing: |
| 143 | +grep -rl "^aliases:" content/cs/ |
| 144 | + |
| 145 | +# generalized across all languages — an alias not prefixed with its own lang code is a hijack: |
| 146 | +for d in content/*/; do l=$(basename "$d"); [ "$l" = en ] && continue |
| 147 | + grep -rhoE '^\s+- /\S+' "$d" 2>/dev/null | grep -v "^ - /$l/" && echo "HIJACK RISK in $l" |
| 148 | +done |
| 149 | +``` |
| 150 | + |
| 151 | +Then `hugo` and confirm: the build is WARN/ERROR-free, English `Aliases` count is |
| 152 | +unchanged (26 — 25 content aliases plus Hugo's generated `/en/` → `/` redirect), and |
| 153 | +`<out>/maps/index.html` still redirects to an English target (`grep -o 'url=[^"]*'` |
| 154 | +must not contain a language prefix). |
| 155 | + |
| 156 | +Summarize: files created, files patched, files deleted, per language. If any subagent |
| 157 | +failed, list those jobs so they can be retried. Finally delete the temporary |
| 158 | +`.translation-plan.json`. |
| 159 | + |
| 160 | +## Notes |
| 161 | +- The hash format is identical to web-dbgate-io's, so the two repos' skills behave the same. |
| 162 | +- `skipTranslation: true` in an English article's front matter excludes it (and causes |
| 163 | + existing translations to be deleted). A translated file with `skipTranslation: true` is |
| 164 | + protected from deletion. |
| 165 | +- `set_hash.py` also stamps a `language:` field. It is inert under |
| 166 | + translation-by-directory but is kept for parity with web-dbgate-io. |
0 commit comments