Follow the core's language roll-out in the extension by a script and a test, instead of by hand #10

Closed
opened 2026-09-30 15:19:46 +00:00 by tiagoagueda · 2 comments
Owner

The extension ships 40 catalogues under src/_locales/, and a Postulo left with its defaults offers the same 40: en-gb plus the 39 catalogues started in the core's src/postulo/locale/status.json, four codes spelled differently (en-gb→en, fr-fr→fr, pt-pt→pt_PT, pt-br→pt_BR). The match is kept by hand. test/i18n.test.mjs hard-codes the list as OFFERED; the README says both “the 39 European catalogues the core has complete” and “A language joins here when the core starts offering it”; nothing reads the core's list at build, test or run time, and CI checks out only this repository. Commit 08774a2 pairs the version number and nothing else (postulo/postulo#186). This issue asks for the extension's languages to follow the core's roll-out by a rule that is checked, not remembered.

The two lists already follow different rules

The core offers a language once its catalogue is started (translated above 0 in status.json; language_choices() in src/postulo/accounts/forms.py), minus any an administrator takes off the list (SiteSettings.offered_languages). The extension's test calls its list “the core's catalogues at 100%” and demands all 115 keys, non-empty, in every locale, so a partly translated extension language cannot exist.

They split the day the core starts a catalogue. It already holds 29 with nothing translated, the African set added for postulo/postulo#70, among them Arabic, written right to left; which languages come next is postulo/postulo#269, by number of speakers.

What the extension should do

  1. A script, in the manner of scripts/icons.py, reads the core beside this one (../postulo): src/postulo/core/languages.py (NATIVE_NAMES and RTL, importable without Django) and src/postulo/locale/status.json, where en-gb, the source, has no row and is always in. It writes a committed list of the languages to ship, each with its folder name, direction and own name, so neither build nor tests need Python or the core.
  2. The code mapping is written down once, in that script: the four spellings above, every other code unchanged. A code with a region or script it has no entry for stops the script rather than being guessed at.
  3. test/i18n.test.mjs asserts, in place of OFFERED, that the _locales folders equal the committed list and that each catalogue's direction matches it.
  4. Under the rule recommended below, a language joins the extension in the release in which the core starts it, all 115 keys filled at once as drafts, so the “every key in every locale” rule can stay.
  5. The first right-to-left language (of the core's 69, only ar) brings a right-to-left check: src/lib/i18n.js sets dir from direction and src/style.css names no physical side, but no test renders a page that way.

Decisions for this repository

  • What “the current Postulo core server” means. Read here as the core release the extension is released beside (recommended): the extension carries its version (0.3.0, beside the core's v0.3.0) and its catalogues are part of the reviewed package, so its languages change only with a release; an administrator's narrowing plays no part. The alternative is the instance it is connected to, at run time: that needs the server (below), and could only narrow the shipped catalogues, in practice the list #11 offers.
  • Which core catalogues to follow. Started, as the core offers them (recommended: every language the core offers, from the same release, in draft), or complete only (percent 100), which keeps the test's comment true but has the extension lag the core.
  • Checking in CI. Optionally, CI also checks out postulo/postulo at the tag matching package.json's version (the core tags its releases; this repository does not), runs the script and fails on a difference. The core's main can be a catalogue ahead of its last tag, so this suits a release check better than every push.
  • Weblate. The _locales are not a Weblate component, and its WebExtension JSON format cannot mark a string as a draft. For later, not this issue.

Shipped is not always reachable, in Chromium

Chrome's extension i18n documentation says “If you use an unsupported locale, Google Chrome ignores it”, and 13 of the 40 folders are not on its list: bs, cy, eu, ga, gl, hy, is, ka, lb, mk, mt, nb (it lists no) and sq; of the 29 waiting in the core, only am, ar and sw are. Chromium's code (extensions/common/extension_l10n_util.cc) skips only folders that are not ICU locales, so which are dropped is unclear. And getMessage follows the browser's language, while Chrome on Windows, macOS and Linux has no interface in bs, cy, eu, gl, hy, is, ka, mk or sq, and none in ga, lb or mt on any platform (build/config/locales.gni). None of this is verified in a real browser. If it holds, a faithful mirror still misses those speakers in Chrome; #11 reaches them.

Needs from the server

Following one instance at run time needs the core to say which languages it offers. No call does: /healthz returns status, database and version, and GET /me returns no language. Not filed on postulo/postulo; the per-release list works with the server as it is.

Done when

  • A script lists the languages to ship from the core, the list is committed, the mapping is in one place, and npm test fails when _locales differs from it.
  • The README's “Languages” section states the rule chosen, once, and how the list is kept; its “Layout” table names the script. PRIVACY.md does not change: nothing new is sent or stored.

From the list of extension improvements of 2026-09-30.

The extension ships 40 catalogues under `src/_locales/`, and a Postulo left with its defaults offers the same 40: `en-gb` plus the 39 catalogues started in the core's `src/postulo/locale/status.json`, four codes spelled differently (`en-gb`→`en`, `fr-fr`→`fr`, `pt-pt`→`pt_PT`, `pt-br`→`pt_BR`). The match is kept by hand. `test/i18n.test.mjs` hard-codes the list as `OFFERED`; the README says both “the 39 European catalogues the core has complete” and “A language joins here when the core starts offering it”; nothing reads the core's list at build, test or run time, and CI checks out only this repository. Commit 08774a2 pairs the version number and nothing else (postulo/postulo#186). This issue asks for the extension's languages to follow the core's roll-out by a rule that is checked, not remembered. ## The two lists already follow different rules The core offers a language once its catalogue is started (`translated` above 0 in `status.json`; `language_choices()` in `src/postulo/accounts/forms.py`), minus any an administrator takes off the list (`SiteSettings.offered_languages`). The extension's test calls its list “the core's catalogues at 100%” and demands all 115 keys, non-empty, in every locale, so a partly translated extension language cannot exist. They split the day the core starts a catalogue. It already holds 29 with nothing translated, the African set added for postulo/postulo#70, among them Arabic, written right to left; which languages come next is postulo/postulo#269, by number of speakers. ## What the extension should do 1. A script, in the manner of `scripts/icons.py`, reads the core beside this one (`../postulo`): `src/postulo/core/languages.py` (`NATIVE_NAMES` and `RTL`, importable without Django) and `src/postulo/locale/status.json`, where `en-gb`, the source, has no row and is always in. It writes a committed list of the languages to ship, each with its folder name, direction and own name, so neither build nor tests need Python or the core. 2. The code mapping is written down once, in that script: the four spellings above, every other code unchanged. A code with a region or script it has no entry for stops the script rather than being guessed at. 3. `test/i18n.test.mjs` asserts, in place of `OFFERED`, that the `_locales` folders equal the committed list and that each catalogue's `direction` matches it. 4. Under the rule recommended below, a language joins the extension in the release in which the core starts it, all 115 keys filled at once as drafts, so the “every key in every locale” rule can stay. 5. The first right-to-left language (of the core's 69, only `ar`) brings a right-to-left check: `src/lib/i18n.js` sets `dir` from `direction` and `src/style.css` names no physical side, but no test renders a page that way. ## Decisions for this repository - **What “the current Postulo core server” means.** Read here as the core release the extension is released beside (recommended): the extension carries its version (0.3.0, beside the core's `v0.3.0`) and its catalogues are part of the reviewed package, so its languages change only with a release; an administrator's narrowing plays no part. The alternative is the instance it is connected to, at run time: that needs the server (below), and could only narrow the shipped catalogues, in practice the list #11 offers. - **Which core catalogues to follow.** Started, as the core offers them (recommended: every language the core offers, from the same release, in draft), or complete only (`percent` 100), which keeps the test's comment true but has the extension lag the core. - **Checking in CI.** Optionally, CI also checks out postulo/postulo at the tag matching `package.json`'s version (the core tags its releases; this repository does not), runs the script and fails on a difference. The core's main can be a catalogue ahead of its last tag, so this suits a release check better than every push. - **Weblate.** The `_locales` are not a Weblate component, and its WebExtension JSON format cannot mark a string as a draft. For later, not this issue. ## Shipped is not always reachable, in Chromium Chrome's extension i18n documentation says “If you use an unsupported locale, Google Chrome ignores it”, and 13 of the 40 folders are not on its list: `bs`, `cy`, `eu`, `ga`, `gl`, `hy`, `is`, `ka`, `lb`, `mk`, `mt`, `nb` (it lists `no`) and `sq`; of the 29 waiting in the core, only `am`, `ar` and `sw` are. Chromium's code (`extensions/common/extension_l10n_util.cc`) skips only folders that are not ICU locales, so which are dropped is unclear. And `getMessage` follows the browser's language, while Chrome on Windows, macOS and Linux has no interface in `bs`, `cy`, `eu`, `gl`, `hy`, `is`, `ka`, `mk` or `sq`, and none in `ga`, `lb` or `mt` on any platform (`build/config/locales.gni`). None of this is verified in a real browser. If it holds, a faithful mirror still misses those speakers in Chrome; #11 reaches them. ## Needs from the server Following one instance at run time needs the core to say which languages it offers. No call does: `/healthz` returns `status`, `database` and `version`, and `GET /me` returns no language. Not filed on postulo/postulo; the per-release list works with the server as it is. ## Done when - A script lists the languages to ship from the core, the list is committed, the mapping is in one place, and `npm test` fails when `_locales` differs from it. - The README's “Languages” section states the rule chosen, once, and how the list is kept; its “Layout” table names the script. PRIVACY.md does not change: nothing new is sent or stored. From the list of extension improvements of 2026-09-30.
Author
Owner

Final decision on translations (maintainer, 2026-10-01)

  1. Every language is scaffolded: each has its catalogue, with a slot for every string.
  2. A development build requires every string to be present in en-GB, fr-FR and pt-PT. A draft counts.
  3. A release requires every string to be present in the languages selected for that release. A draft counts.
  4. 0.4.0 requires no additional languages.
  5. 0.5.0 additionally requires fr-CA and en-CA; then, eventually, any language of the European continent still missing (Russian excepted); and, eventually, Chinese and Arabic. Those last two may move to 0.6.0 if the load on 0.5.0 becomes too great.

What this changes here

  • The rule the extension follows is now stated. The script and the test proposed here should hold the extension to the same two gates as the core: every sentence present in en-GB, fr-FR and pt-PT on a development build, and in the languages selected for a release when the extension is released. A draft counts.
  • Scaffolded is not shipped. The core will carry a catalogue for every language; the extension should ship a _locales/ folder only for a language the core offers, as this issue already proposes, not for every scaffolded one. The browser has no notion of an empty catalogue.
  • Next to arrive: fr_CA and en_CA (0.5.0), then the European languages still missing, then Chinese and Arabic. Arabic is the first right-to-left language the popup has to lay out.
  • The test's wording, "the core's catalogues at 100%", already matches the decision: a language joins complete, as drafts.
**Final decision on translations (maintainer, 2026-10-01)** 1. **Every language is scaffolded**: each has its catalogue, with a slot for every string. 2. **A development build** requires every string to be present in **en-GB, fr-FR and pt-PT**. A draft counts. 3. **A release** requires every string to be present in **the languages selected for that release**. A draft counts. 4. **0.4.0** requires no additional languages. 5. **0.5.0** additionally requires **fr-CA** and **en-CA**; then, eventually, any language of the European continent still missing (Russian excepted); and, eventually, **Chinese** and **Arabic**. Those last two may move to 0.6.0 if the load on 0.5.0 becomes too great. ## What this changes here - **The rule the extension follows is now stated.** The script and the test proposed here should hold the extension to the same two gates as the core: every sentence present in **en-GB, fr-FR and pt-PT** on a development build, and in **the languages selected for a release** when the extension is released. A draft counts. - **Scaffolded is not shipped.** The core will carry a catalogue for every language; the extension should ship a `_locales/` folder only for a language the core offers, as this issue already proposes, not for every scaffolded one. The browser has no notion of an empty catalogue. - **Next to arrive:** `fr_CA` and `en_CA` (0.5.0), then the European languages still missing, then Chinese and Arabic. Arabic is the first right-to-left language the popup has to lay out. - The test's wording, "the core's catalogues at 100%", already matches the decision: a language joins complete, as drafts.
Author
Owner

Follow-up to the final decision on translations (maintainer, 2026-10-01)

  • Chinese: both. Simplified and traditional, two catalogues (zh-Hans, zh-Hant).
  • "European" means the continent, not the European Union. Turkish and Icelandic count; they are already carried.
  • Every component moves with the core, language-wise, plugins and extensions included.

The work is postulo/postulo#347.

**Follow-up to the final decision on translations (maintainer, 2026-10-01)** - **Chinese: both.** Simplified and traditional, two catalogues (`zh-Hans`, `zh-Hant`). - **"European" means the continent, not the European Union.** Turkish and Icelandic count; they are already carried. - **Every component moves with the core, language-wise**, plugins and extensions included. The work is postulo/postulo#347.
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-chromium#10
No description provided.