2026-11-06-crowdin-translation-step-2-full
Crowdin translation, step 2 (full translation): implementation plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Translate all 109 pages of the main documentation and all interface text into German, Arabic, Norwegian, French and Italian, with the check script green on every page.
Architecture: The pilot (step 1) gave the site config, the check script, the glossary and 5 pages in each language. This plan first makes the check script strict (a full-coverage mode, a JSON check, a list of out-of-date pages). Then it translates one language for each merge request. Inside one language, subagents translate by topic folder, and each subagent runs the check after each page.
Tech Stack: Docusaurus 3.9.2 with the faster bundler, Node 20 with node:test, the check script docusaurus-site/scripts/i18n/check-translations.mjs, GitLab CI.
Spec: docs/superpowers/specs/2026-11-04-crowdin-translation-design.md, section 8, step 2. Sections 6.1, 6.3.6 and 6.4 apply.
Global Constraints
- Locales:
de,ar,no,fr,it. Translated page path:docusaurus-site/i18n/<locale>/docusaurus-plugin-content-docs/current/<same path as docs/>. - The rules for the translator are in
docusaurus-site/scripts/i18n/TRANSLATING.md. Every subagent reads them before its first page. - Do not translate the terms of
scripts/i18n/glossary.json, code, URLs, file and image paths, frontmatter keys,slug,id,sidebar_position, admonition keywords. - Keep every link target unchanged. The API reference has its own path in each language (
/<locale>/api-reference/hawkeye), since !20(knowledge-base). - One merge request for each language into
v1.0. Conventional commits, each with the trailerCo-Authored-By: Claude Opus 5.5 <[email protected]>. - No Crowdin call in this step. Do not change
docusaurus-site/crowdin.yml(step 3 does it). - Translation has no fee: Claude Code sessions only. No paid engine, no agency, no Crowdin AI credits.
- Never search the whole disk. Search only the repo.
Facts on 2026-11-06
| Item | Value |
|---|---|
| English pages | 109 (about 49,000 words) |
| Translated pages | 5 in each language (the pilot) |
| Pages to translate | 104 in each language, 520 in total |
| Largest folder | cityscope-previous-platform/: 41 pages, about 20,500 words (42% of the work) |
| Live languages | all 5 are live on help.staging.vianova.dev since !21(knowledge-base) (user's choice, for a demo) |
Rulings (decisions that differ from the spec)
- The live site changes. The spec says "the live site shows no change" for step 2. All 5 languages are live on staging since !21(knowledge-base), so each merge request of this plan shows new translated pages on staging. The done condition becomes: the Chrome check passes on staging after the deploy. Cost if wrong: a reader on staging sees a translation that no native speaker has read. The site is staging only, so the risk is low.
- Folder
cityscope-previous-platform/goes last in each language. It documents the old platform, and it is 42% of the work. The user can remove it from the scope before Task 5 starts, and no translated work is lost. Open question for the user, below.
Open fields (the user fills them)
| Field | Value | Needed by |
|---|---|---|
Translate cityscope-previous-platform/ (41 pages)? | open, default: yes | Task 5 |
| Native reader, German | open | not this plan (step 4) |
| Native reader, Arabic | open | not this plan (step 4) |
| Native reader, Norwegian | open | not this plan (step 4) |
| Native reader, French | open | not this plan (step 4) |
| Native reader, Italian | open | not this plan (step 4) |
Review Focus
- An English page changes on
v1.0while its translation is in work. Expected: the out-of-date list shows the page, and the translator updates it before the merge request merges. Test in Task 1 (staleTranslations). - A page that uses a relative link (
../maps/satellite-view.md) or an image with a relative path. Expected: the translated page keeps the same target, and the built page shows the image. Checked by thebuild-localesjob and the Chrome check in Task 4. - A JSON file of
i18n/<locale>/that misses a key that English has (for example a new sidebar category). Expected: the check fails with rulejson-missing. Test in Task 1. - A page with a table that has inline code or a link in a cell, in Arabic. Expected: the table reads right to left, and the code stays left to right. Chrome check in Task 4.
- A subagent that stops in the middle of a folder. Expected: the coverage report shows the missing pages, and the next run translates only those pages. Test in Task 1 (
coverage).
File map
| File | Responsibility |
|---|---|
docusaurus-site/scripts/i18n/check-translations.mjs | Adds --require-all, the JSON check and the coverage line |
docusaurus-site/scripts/i18n/stale-translations.mjs | Lists translated pages whose English page changed after the translation |
docusaurus-site/scripts/i18n/*.test.mjs | Tests of the two scripts |
docusaurus-site/scripts/i18n/TRANSLATING.md | Adds the batch method and the out-of-date check |
docusaurus-site/i18n/<locale>/docusaurus-plugin-content-docs/current/** | The 104 translated pages of each language |
docusaurus-site/i18n/<locale>/**/*.json | Interface text of each language, including the API reference sidebar labels |
Task 1: A stricter check script
Files:
- Modify:
docusaurus-site/scripts/i18n/check-translations.mjs - Create:
docusaurus-site/scripts/i18n/stale-translations.mjs,docusaurus-site/scripts/i18n/stale-translations.test.mjs - Modify:
docusaurus-site/scripts/i18n/check-translations.test.mjs,docusaurus-site/package.json(script"i18n:stale": "node scripts/i18n/stale-translations.mjs")
Interfaces:
-
Consumes:
checkSite(siteDir, locales, glossary)andlistPages(dir)ofcheck-translations.mjs. -
Produces:
coverage(siteDir: string, locales: string[]): Promise<Record<string, {translated: number, total: number, missing: string[]}>>.checkJson(siteDir: string, locales: string[]): Promise<Problem[]>. For each JSON file ofi18n/en/(recursive), the file ofi18n/<locale>/must exist and have every key of the English file. Rules:json-missing-file,json-missing. Extra keys pass (Docusaurus ignores them).- CLI:
node scripts/i18n/check-translations.mjs [--require-all] [--skip-folder <folder>] [locale...]. It always prints one lineCOVERAGE <locale> <translated>/<total>. With--require-all, each missing page is a problem with rulemissing, and thecheckJsonproblems count. Without--require-all,checkJsonproblems print asWARNlines and do not change the exit code. So the CI jobcheck-translations(no flag, all 5 languages) does not fail on a language that is not started yet.--skip-folderremoves one top folder ofdocs/fromtotaland frommissing(for the open field aboutcityscope-previous-platform/). staleTranslations(repoDir: string, siteDir: string, locales: string[]): Array<{locale: string, file: string, englishCommit: string}>. A page is out of date whengit log -1 --format=%ct -- docs/<file>is newer than the same for the translated file. UseexecFileSync('git', [...]), never a shell string.- CLI:
npm run i18n:stale [locale...]prints<locale> <file> <englishCommit>for each page, orOK no out-of-date page.
-
Step 1: Write the failing tests.
coverage counts missing pages: temp dir withdocs/a.md,docs/b.mdand onlya.mdinde→{de: {translated: 1, total: 2, missing: ['b.md']}}.require-all reports a missing page: the same dir, CLI with--require-all de→ exit code 1, and the output hasde b.md missing.json with all keys passes:i18n/en/code.json{"x": {"message": "Hi"}}andi18n/de/code.json{"x": {"message": "Hallo"}}→[].json missing key fails:de/code.jsonis{}→ one problem, rulejson-missing, detail hasx.json missing file fails: node/code.json→ rulejson-missing-file.json gaps only warn without require-all: CLI withdeon the dir of the last test → exit code 0, and the output has aWARNline.skip-folder reduces the total:docs/a.md,docs/old/b.md,de/a.md, CLI--require-all --skip-folder old de→COVERAGE de 1/1, exit code 0.stale page is listed: temp git repo (git init,user.email [email protected]), commitdocs/a.mdand itsdecopy, then commit a change todocs/a.mdwith a later date (GIT_COMMITTER_DATE) → one entry,file === 'a.md'.fresh page is not listed: the translated file is committed after the English file →[].
-
Step 2: Run
cd docusaurus-site && npm run i18n:test. Expected: the 9 new tests fail (functions not defined). -
Step 3: Implement the functions and the CLI flags above. The CI job
check-translationskeepsnpm run i18n:checkwith no--require-all, so a missing page still shows English. -
Step 4: Run
npm run i18n:test. Expected: all tests pass. Runnpm run i18n:check. Expected:COVERAGE de 5/109(and the same for the other 4 languages), thenWARNlines for the JSON files that the pilot did not translate (for example the API reference sidebar labels), andOK. Task 3 Step 2 fixes the warnings of each language. -
Step 5: Run
npm run i18n:stale. Expected: a list (maybe empty) of pilot pages whose English page changed after 2026-11-04. Task 3 updates them. -
Step 6: Commit
feat(i18n): report translation coverage, JSON keys and out-of-date pages. Push, open a merge request intov1.0with/mr-describe, run/review-board, merge through the normal merge path.
Task 2: Translator method for large batches
Files:
-
Modify:
docusaurus-site/scripts/i18n/TRANSLATING.md -
Step 1: Add a section "Large batches" in strict ASD-STE100:
- Before a batch, run
npm run i18n:stale <locale>and update each listed page first. - Translate one topic folder at a time. After each page, run
npm run i18n:check <locale>. - Keep the words of the earlier pages of the same language: read 2 translated pages of the language before the first page of a new folder.
- At the end of the language, run
npm run i18n:check -- --require-all <locale>. The result must beOK. - Write
npx docusaurus write-translations --locale <locale>before the JSON work, so that every new key is in the file. Then translate eachmessage. This covers the sidebar labels of docs, recipes, API reference and changelog (spec 6.1.3).
- Before a batch, run
-
Step 2: Commit
docs(i18n): add the method for large translation batches, in the same merge request as Task 1.
Task 3: One language (do this task 5 times)
Order of languages: de, no, ar, fr, it (the clients in Germany and in the Nordic countries wait first, and Arabic needs the most layout checks). One branch and one merge request for each language: feat/i18n-full-<locale>(knowledge-base).
Files:
- Create:
docusaurus-site/i18n/<locale>/docusaurus-plugin-content-docs/current/**(104 pages). - Modify: every JSON file of
docusaurus-site/i18n/<locale>/.
Interfaces:
-
Consumes: the CLI flags of Task 1, the method of Task 2.
-
Step 1: Run
npm run i18n:stale <locale>, and update each listed pilot page. -
Step 2: Run
npx docusaurus write-translations --locale <locale>. Translate everymessageof every JSON file. Runnpm run i18n:check <locale>. Expected: nojson-problem. -
Step 3: Translate the topic folders, in this order:
get-started,maps,zones,data-layers,analysis-and-dashboards,export-and-share,regulations-and-reports,shared-mobility,citizen-reporting-app,administration,developers,tools,data-feeds-and-quality,data-products. One subagent for each group of about 10,000 words is allowed. Each subagent gets: the folder list,TRANSLATING.md, the glossary, and 2 translated pages of the language as a word reference. Commit after each folder:feat(i18n): translate <folder> into <language>. -
Step 4: Folder
cityscope-previous-platform/, only when the open field "Translatecityscope-previous-platform/" is yes. Same method. -
Step 5: Verify. Run
npm run i18n:check -- --require-all <locale>. Expected:COVERAGE <locale> 109/109andOK 109 files checked. Without the old-platform folder, add--skip-folder cityscope-previous-platform: expectedCOVERAGE <locale> 68/68. ThenDOCUSAURUS_NO_PERSISTENT_CACHE=1 npx docusaurus build --locale <locale> --out-dir build. Expected:[SUCCESS], and no new broken-link warning for/<locale>/compared with the English build. -
Step 6: Local Chrome check on
npm run serve -- --port 3077. Open 5 pages of the language: one from each ofget-started,maps,data-products,developers,data-feeds-and-quality. Check: text in the language, images show, tables, code blocks, the sidebar labels, the search box finds a word of the language. Forar, also do the Arabic list of spec section 8. Save a screenshot of each page. -
Step 7: Push. Open the merge request with
/mr-describe; put the coverage line, the check output and the screenshots in it. Wait on CI withGLW_REPO=vianova/knowledge-base gl-wait <pipeline> check-translations build build-locales build-all-locales. Run/review-board, then the normal merge path. -
Step 8: Staging check after the
v1.0deploy: the same 5 pages onhttps://help.staging.vianova.dev/<locale>/...return 200 and show the language.
Task 4: All languages done
- Step 1: Run
npm run i18n:check -- --require-all. Expected: 5COVERAGElines at 109/109, thenOK 545 files checked. Without the old-platform folder:npm run i18n:check -- --require-all --skip-folder cityscope-previous-platform, expected 68/68 andOK 340 files checked. - Step 2: Run
npm run i18n:stale. Expected:OK no out-of-date page. - Step 3: Update the focus with
focus park "step 3: Crowdin connection waits for the Crowdin admin".
Done condition (spec section 8, step 2, with ruling 1)
The check script is green with --require-all on all 5 languages, every language merge request is merged, and the staging check of Task 3 Step 8 passed for each language.
Not in this plan
- The Crowdin project,
crowdin.yml, the Crowdin CI jobs and the one-writer check (step 3). - The native review (step 4). The 5 reader names stay open until then.
Updated about 11 hours ago
