An official plugin can compile catalogues but cannot make them, so it has two languages and core has sixty-eight #187
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
security
tier
1
tier
2
tier
3
tier/4
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Postulo/postulo#187
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The rule, stated once for both
The rule is the same on both sides of the plugin boundary, and #182 just set the core half:
languages.SOURCE = "en-gb") and has no catalogue.fr_FRandpt_PT. Enough to see a change in three languages and catchwhat only shows up in one.
An official plugin follows it exactly. The full translation duty has not gone anywhere; it
has moved to the release.
Why plugins have two languages and it is not laziness
Core never translates a plugin's strings — that is the plugin-locale rule and it is right.
But the tooling never came with the duty:
scripts/compile_messages.pyfr_FR,pt_PTcompile_messages.pyfr_FR,pt_PTcompile_messages.pyfr_FR,pt_PTcompile_messages.pyfr_FR,pt_PTcompile_messages.pyfr_FR,pt_PTEvery one of those five scripts is byte-identical — same MD5, copy-pasted five times —
and not one of them can extract. They turn a
.pointo a.moand nothing creates orupdates the
.poin the first place. Core hasscripts/messages.pywithextract,extract --check,checkand catalogue-set discovery; a plugin repository has none of it.So the two catalogues each plugin carries were made by hand, and the other sixty-six were
never made at all. "In par with core" is not a matter of filling in translations — there
is nothing to fill in. That is the thing to fix first.
Where the tool should live
Each plugin already depends on core in its dev group:
Runtime dependencies are just
httpxand friends — core is a development dependency only,which is exactly the right shape for a translation tool. So core can ship one command a
plugin repository runs against itself, and the five identical
compile_messages.pyfilescollapse into it rather than becoming six.
What it has to do, because a plugin is not a Django project:
locale/without a settings module.sixty-eight slots core does and a release sweep has somewhere to write.
extract,extract --checkandcheck, behaving as core's do, so the habit transfers.exists (no GNU gettext needed).
Each plugin needs its own release gate
Core's
uv run pytest -m releasecannot see a plugin's catalogues — the plugin-localerule guarantees it. Without the same marker and the same checklist line in each plugin
repository, "full translation at release" quietly means plugins are never fully translated.
So each official plugin gets:
releasemarker in itspyproject.toml, deselected by default;uv run pytest -m releaseas step one of its own release checklist.Two repositories that are not like the others
postulo-chromium and postulo-firefox use WebExtension
_locales/<lang>/messages.json,not gettext. Chromium already carries all thirty-nine; Firefox commits none and builds them
from the vendored Chromium source. The rule applies to both — three while working, the full
set at release — but none of the tooling above does, and their release gate has to be written
against
messages.json.postulo-mcp has no catalogue at all, and this is the one place the rule should probably
not simply be applied. Its user-facing strings are tool descriptions and prompts read by a
model, not a person; a French tool description reaching an agent whose user writes English
is worse than an English one. Its README and command-line messages are a different question.
Decide which of the two it has, and if the answer is "no catalogue, deliberately", write that
in the repository so nobody adds one for consistency.
Related
releasemarker comes from.their catalogues, and both land per repository at each plugin's first release.
Settled for postulo-mcp: English only, no catalogue — the one official plugin exempt from
this issue's rule, deliberately.
Its strings are tool descriptions, resource descriptions and prompts read by a model, and a
French tool description reaching an agent whose user writes English is worse than the English
one. Recorded with the reasoning, and with what would reopen it, at postulo/postulo-mcp#1.
Every other official plugin follows the rule in full. The exemption is about the audience of
these particular strings, not about translation mattering less.
Landed across the repositories.
Core —
f4263a2dconmain:scripts/messages.pyispostulo.core.messages_toolnow, pointed at a repository, installed as the console scriptpostulo-messageswherever Postulo is a dependency. It readspyproject.tomlfor the project's name, expects the package undersrc/, and runsextract,extract --check,checkandcompileagainst that package'slocale/, creating the same sixty-eight slots core has. The wiki's Writing a plugin tells a plugin to use it.Every Python plugin — helloworld
9397274, imap23cef46, apprise57a5e91, davb15b164, paperless9c05133: the copiedcompile_messages.pyis gone,extractwrote the sixty-eight catalogues with French and European Portuguese kept and Brazilian Portuguese seeded from the European (draft), andtests/test_catalogues.pyis the gate — catalogues current, placeholders agreeing, the two working languages complete on every commit, the twenty-four European Union languages complete behinduv run pytest -m release, registered as a marker and deselected by default. The full sweep for each plugin happens at its first release, as the rule says.postulo-mcp is exempt, as decided on its #1; the two browser extensions keep their
messages.jsonand are not covered by this tool.