Wiki in several languages: every page through Weblate, published at wiki.tiagoagueda.com #1215

Open
opened 2026-11-01 14:58:46 +00:00 by tiagoagueda · 1 comment
Owner

The wiki in several languages, translated in Weblate and published at https://wiki.tiagoagueda.com. This follows #352: its question is answered, and the answer goes further than its three pages. A trial on Getting started is live and is written up below.

Decided (maintainer, 2026-11-01)

  • English is the wiki's source language. Pages are still written and committed in postulo.wiki.
  • French (France, fr-FR, not fr-CA) and European Portuguese (pt-PT) are the additional languages. The maintainer reviews them, and a release waits for them.
  • The whole wiki is translated, in stages. A release waits for fr-FR and pt-PT only on the pages of the stages already open, not on the whole wiki at once.
  • The most spoken languages are scaffolded, and no release waits for them: zh-Hans, hi, es, ar, bn, ur, id, de, ja, pt-BR. Russian is left out, as it was for the app.
  • The site is Zensical (from the authors of Material for MkDocs, whose fixes end in November 2026). It is deployed from docker-recipes wiki/ as Dockhand stack 137 on ragnar. It replaces the HTML half of scripts/wiki_site.py.

How it works (as tried on 2026-11-01)

In Weblate (project postulo):

  • One component per page, using the Markdown file format, plus wiki-site for site.json. Today these are wiki-getting-started and wiki-site (linked to it).
  • File mask lang/*/<Page>.md, template <Page>.md, language code style bcp.
  • The components push straight to the wiki's main with vcs: git and no push branch. A Forgejo wiki has no branches and no pull requests, so Weblate's usual flow (#349) is not possible there.
  • The translations team was given wiki write (repo.wiki). Without it the push answers "Repository not found".
  • The project alias fr-FR:fr makes Weblate write French to lang/fr-FR/ (France French, as the app's fr_FR) instead of lang/fr/. Portuguese comes out as lang/pt-PT/ without an alias.
  • Squash add-on is on; machine drafting is off.

On the site:

  • English is at /, and each published language is under /<tag>/, with its own menu, search, interface language and right-to-left setting.
  • A page with no translation, or one that is still at least nine-tenths English, is shown in English with a banner in the reader's language. The banner text comes from lang/<tag>/site.json.
  • A translated page with English paragraphs left in it gets the "partial" banner.
  • The language selector links every language. A first visit to / is sent to the browser's language. That redirect lives in nginx and so is not part of the Postulo repo.
  • fr-FR and pt-PT are always published. A scaffolded language appears once one of its pages is translated.

What the trial showed about Weblate's Markdown format:

  • It is a "convert" format. Each paragraph, heading, list item and table cell is a unit, and the targets of links and images become numbered placeholders ({1}).
  • Saving rebuilds the translated file from the English template. An untranslated paragraph is written in English, and drafts are written too. So a reader always gets a whole page, and drafts count, as they do for the app.
  • Drafts stay drafts when Weblate rescans after the English changes. The French drafts survived two English edits.
  • A new translation starts as English copies flagged needs rewriting. In one case the units only appeared after a file-scan.
  • manage_units must be off when the component is created, or Weblate refuses it.
  • A list written as one paragraph becomes one unit. Getting started's Settings list had seven of its eight items run into the first, joined by " - " in mid-line. Every renderer showed one bullet, and Weblate offered it as one 900-word string. Fixed in ef0a3e0 and 81387cf. Check each page for this before its component is created.
  • The site reads Markdown the way Python-Markdown does, not CommonMark. A code span wrapped so that the next line starts with a block tag (<address>) swallowed the rest of Writing a plugin (fixed in 8d2d176). The site build does not catch this, so scan for it alongside the glued lists.

What is left

1. Components for every page, stage by stage, each checked first for the two traps above:

  • Stage 1, before an instance: Home, Installing Postulo, Getting started (done), plus _Sidebar.md, which gives each language its menu titles.
  • Stage 2, using it: Installing the app, Listings, Tracking applications, Capturing postings, Insights, Reports, Your career record, CVs, Cover letters, Files and what you sent.
  • Stage 3, running it: Configuration, Accounts and invitations, Backups and your data, Hardening, Accessibility, Health, metrics and logs, Troubleshooting.
  • Stage 4, building on it: The capture API, Webhooks, Writing a plugin, Design, Roadmap.

2. Languages

  • fr-FR and pt-PT in every component.
  • The ten scaffolded languages in every component. When one becomes published, add it to the redirect in docker-recipes wiki/nginx.conf.

3. Postulo's own tools

  • scripts/weblate.py acts on every component of the project, and two commands would break the wiki ones. configure --round-trip would move them to the pull-request flow, and addons would add machine drafting to them. Both must skip wiki-* (or the wiki gets a Weblate category of its own).
  • scripts/wiki_site.py: retire build. stale and stamp are replaced by Weblate's own per-paragraph tracking. Keep components only if it also gives the settings above (vcs, push, alias), or drop it. Update tests/test_wiki_site.py to match.
  • docs/TRANSLATING.md § The wiki in other languages and CONTRIBUTING.md: the way chosen here, the stages and the traps.
  • #345's second section names this way; #352 closes pointing here.

4. The release gate

  • A release refuses to go out while a page of an open stage has an empty paragraph in fr-FR or pt-PT. Weblate knows this per component, so the check reads its API (scripts/release_tools.py, which already reads the release's languages). Decide: do drafts count, as they do for the app's catalogues, or must the maintainer have reviewed them?

5. A "not yet reviewed" banner (to decide)

  • Only Weblate knows which translated paragraphs are drafts, and its API needs a key (anonymous reads get 401). To show "this translation has not been reviewed yet", the site build needs a read-only Weblate token. That means a Vaultwarden item and the Dockhand env of stack 137, which has no secrets today. Without it, a draft looks the same as a reviewed translation on the site.

Done when

  • Every page has its component, fr-FR and pt-PT are in all of them, and the ten scaffolded languages too.
  • A release cannot go out with a gap in fr-FR or pt-PT on an open stage's pages.
  • scripts/weblate.py leaves the wiki components alone, wiki_site.py no longer builds HTML, and the docs describe the way the wiki is translated.

Refs #352, #345, #347, #349, #706.

The wiki in several languages, translated in Weblate and published at **https://wiki.tiagoagueda.com**. This follows #352: its question is answered, and the answer goes further than its three pages. A trial on *Getting started* is live and is written up below. ## Decided (maintainer, 2026-11-01) - **English** is the wiki's source language. Pages are still written and committed in `postulo.wiki`. - **French (France, `fr-FR`, not `fr-CA`) and European Portuguese (`pt-PT`)** are the additional languages. The maintainer reviews them, and **a release waits for them**. - **The whole wiki is translated, in stages.** A release waits for fr-FR and pt-PT only on the pages of the stages already open, not on the whole wiki at once. - **The most spoken languages are scaffolded, and no release waits for them:** `zh-Hans`, `hi`, `es`, `ar`, `bn`, `ur`, `id`, `de`, `ja`, `pt-BR`. Russian is left out, as it was for the app. - **The site is Zensical** (from the authors of Material for MkDocs, whose fixes end in November 2026). It is deployed from docker-recipes `wiki/` as Dockhand stack 137 on ragnar. It replaces the HTML half of `scripts/wiki_site.py`. ## How it works (as tried on 2026-11-01) **In Weblate** (project `postulo`): - One component per page, using the *Markdown file* format, plus `wiki-site` for `site.json`. Today these are `wiki-getting-started` and `wiki-site` (linked to it). - File mask `lang/*/<Page>.md`, template `<Page>.md`, language code style `bcp`. - **The components push straight to the wiki's `main`** with `vcs: git` and no push branch. A Forgejo wiki has no branches and no pull requests, so Weblate's usual flow (#349) is not possible there. - **The `translations` team was given wiki write** (`repo.wiki`). Without it the push answers "Repository not found". - **The project alias `fr-FR:fr`** makes Weblate write French to `lang/fr-FR/` (France French, as the app's `fr_FR`) instead of `lang/fr/`. Portuguese comes out as `lang/pt-PT/` without an alias. - Squash add-on is on; machine drafting is off. **On the site:** - English is at `/`, and each published language is under `/<tag>/`, with its own menu, search, interface language and right-to-left setting. - A page with no translation, or one that is still at least nine-tenths English, is shown in English with a banner in the reader's language. The banner text comes from `lang/<tag>/site.json`. - A translated page with English paragraphs left in it gets the "partial" banner. - The language selector links every language. A first visit to `/` is sent to the browser's language. That redirect lives in nginx and so is not part of the Postulo repo. - fr-FR and pt-PT are always published. A scaffolded language appears once one of its pages is translated. **What the trial showed about Weblate's Markdown format:** - **It is a "convert" format.** Each paragraph, heading, list item and table cell is a unit, and the targets of links and images become numbered placeholders (`{1}`). - **Saving rebuilds the translated file from the English template.** An untranslated paragraph is written in English, and drafts are written too. So a reader always gets a whole page, and drafts count, as they do for the app. - **Drafts stay drafts** when Weblate rescans after the English changes. The French drafts survived two English edits. - A new translation starts as English copies flagged *needs rewriting*. In one case the units only appeared after a `file-scan`. - `manage_units` must be off when the component is created, or Weblate refuses it. - **A list written as one paragraph becomes one unit.** *Getting started*'s Settings list had seven of its eight items run into the first, joined by " - " in mid-line. Every renderer showed one bullet, and Weblate offered it as one 900-word string. Fixed in `ef0a3e0` and `81387cf`. **Check each page for this before its component is created.** - **The site reads Markdown the way Python-Markdown does, not CommonMark.** A code span wrapped so that the next line starts with a block tag (`<address>`) swallowed the rest of *Writing a plugin* (fixed in `8d2d176`). The site build does not catch this, so scan for it alongside the glued lists. ## What is left **1. Components for every page, stage by stage**, each checked first for the two traps above: - [ ] Stage 1, before an instance: *Home*, *Installing Postulo*, *Getting started* (done), plus `_Sidebar.md`, which gives each language its menu titles. - [ ] Stage 2, using it: *Installing the app*, *Listings*, *Tracking applications*, *Capturing postings*, *Insights*, *Reports*, *Your career record*, *CVs*, *Cover letters*, *Files and what you sent*. - [ ] Stage 3, running it: *Configuration*, *Accounts and invitations*, *Backups and your data*, *Hardening*, *Accessibility*, *Health, metrics and logs*, *Troubleshooting*. - [ ] Stage 4, building on it: *The capture API*, *Webhooks*, *Writing a plugin*, *Design*, *Roadmap*. **2. Languages** - [ ] fr-FR and pt-PT in every component. - [ ] The ten scaffolded languages in every component. When one becomes published, add it to the redirect in docker-recipes `wiki/nginx.conf`. **3. Postulo's own tools** - [ ] `scripts/weblate.py` acts on every component of the project, and two commands would break the wiki ones. `configure --round-trip` would move them to the pull-request flow, and `addons` would add machine drafting to them. Both must skip `wiki-*` (or the wiki gets a Weblate category of its own). - [ ] `scripts/wiki_site.py`: retire `build`. `stale` and `stamp` are replaced by Weblate's own per-paragraph tracking. Keep `components` only if it also gives the settings above (vcs, push, alias), or drop it. Update `tests/test_wiki_site.py` to match. - [ ] `docs/TRANSLATING.md` § *The wiki in other languages* and `CONTRIBUTING.md`: the way chosen here, the stages and the traps. - [ ] #345's second section names this way; #352 closes pointing here. **4. The release gate** - [ ] A release refuses to go out while a page of an open stage has an empty paragraph in fr-FR or pt-PT. Weblate knows this per component, so the check reads its API (`scripts/release_tools.py`, which already reads the release's languages). Decide: do drafts count, as they do for the app's catalogues, or must the maintainer have reviewed them? **5. A "not yet reviewed" banner (to decide)** - [ ] Only Weblate knows which translated paragraphs are drafts, and its API needs a key (anonymous reads get 401). To show "this translation has not been reviewed yet", the site build needs a read-only Weblate token. That means a Vaultwarden item and the Dockhand env of stack 137, which has no secrets today. Without it, a draft looks the same as a reviewed translation on the site. ## Done when - Every page has its component, fr-FR and pt-PT are in all of them, and the ten scaffolded languages too. - A release cannot go out with a gap in fr-FR or pt-PT on an open stage's pages. - `scripts/weblate.py` leaves the wiki components alone, `wiki_site.py` no longer builds HTML, and the docs describe the way the wiki is translated. Refs #352, #345, #347, #349, #706.
Author
Owner

Decided (maintainer, 2026-11-01): drafts count. The release gate in item 4 counts a draft (needs editing) as present, as it does for the app's catalogues. A release waits only for empty paragraphs in fr-FR or pt-PT on an open stage's pages, not for the maintainer's review. Reviewing stays the maintainer's work in Weblate and does not block a release.

**Decided (maintainer, 2026-11-01): drafts count.** The release gate in item 4 counts a draft (*needs editing*) as present, as it does for the app's catalogues. A release waits only for **empty** paragraphs in fr-FR or pt-PT on an open stage's pages, not for the maintainer's review. Reviewing stays the maintainer's work in Weblate and does not block a release.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Postulo/postulo#1215
No description provided.