Translations: a working round trip between this repository and its Weblate project on translate.tiagoagueda.com #349
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
performance
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Postulo/postulo#349
Loading…
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?
Requested by the maintainer on 2026-10-01, for every repository: integration and a fully working workflow of string translation with the corresponding translation projects hosted on translate.tiagoagueda.com (Weblate). This is the core's issue and the one the others point at.
Where it stands
As set up on 2026-09-16 (to be checked against the server before starting):
postulo, with 21 components:postulo(src/postulo/locale/*/LC_MESSAGES/django.po), oneplugin-<name>for each of the 15 plugin catalogue sets that were in the tree that day, and one for each of the five plugin repositories. All gettext PO,new_lang: none, POSIX language codes.src/postulo/plugins/*/locale), so at least two have no component.So the first half of a round trip exists for most catalogues, and the second half does not exist for any.
What "fully working" means
The same four things in every repository:
main.docs/TRANSLATING.md) tell a speaker where to go and what happens to what they type.What has to be settled or built in the core
mainmoves.mainis what CI, the dev image and releases run from, which is why a direct push was declined; say which it is, create the token it needs, and record where the token lives.draftflag. A string a machine wrote and no speaker has read carriesdraftin the catalogue (docs/TRANSLATING.md), and the interface's translation bars count it (#312). Weblate knowsfuzzyand its own review states, notdraft. Decide the mapping in both directions: a draft should arrive in Weblate as needing a reader, and a speaker approving it there should clear the flag here.scripts/messages.py extract --checkfails on a catalogue that differs from what the tool writes (header comment,#:references, wrapping, the order of entries). Either Weblate is configured to write exactly that, or the tool normalises what arrives;tests/test_translations.py::test_the_catalogues_are_what_a_fresh_extraction_writesis the test that will say.locale/and no component is a failure somewhere visible.new_lang: none, a language exists in Weblate when its catalogue exists here, so scaffolding every language is what makes each one translatable by a speaker.docs/TRANSLATING.mdand the wiki describe the path from a speaker's point of view. This is the part of #72 that reads "a translator does not need a developer".Done when
scripts/messages.py extract --checkpasses on a catalogue Weblate wrote, and Weblate shows no change afterextractruns on an unchanged tree.docs/TRANSLATING.mdfrom "I speak this language" to "my translation is in the next release" without asking anybody.The other repositories
Each has the same issue, for its own catalogues: postulo/postulo-apprise#8, postulo/postulo-dav#17, postulo/postulo-imap#6, postulo/postulo-paperless#8, postulo/postulo-helloworld#7, postulo/postulo-chromium#13, postulo/postulo-thunderbird#10, postulo/postulo-firefox#6, postulo/postulo-templates#10, postulo/postulo-mcp#5.
What Weblate holds today, read on the server on 2026-10-01 after postulo/postulo#337 renamed Serbian's catalogues to
sr_Cyrl. Nothing was changed there.Weblate has merged nothing since the day it was set up.
AUTO_UPDATEis off and no repository has a webhook, so the nightly task fetches and never merges. Thepostulocheckout is at its 2026-09-16 state, 132 commits behind what it has fetched and 148 behinda6363a397; each plugin component is a commit or two behind. Every component still shows the September catalogue: 2,117 strings inpostulo, where the tree has over 3,000.The
postulocomponent cannot merge at all. It carries a commit of its own,75ad12b6b, from the days after it was set up: one translation made in Weblate (pt-PT, API token: "Token de API" became "Token da API"), with the header rewritten the way Weblate writes one (Last-Translator,Language-Team,X-Generator). Those header lines sit besidePOT-Creation-Date, which everyextractchanges, so mergingmainconflicts inpt_PT/LC_MESSAGES/django.po. Reproduced in a scratch clone: one conflict, in the header; the translation itself merges. This is "one format, two writers" from the body, met for real with a single translation.plugin-paperlesscarries a header-only commit made with it (fa54e1e) and merges cleanly.That translation never reached the repository.
mainstill says "Token de API" in pt-PT and pt-BR; postulo-paperless says "Token da API".What the Serbian rename will do when a merge does happen. Weblate reads a directory named
sr_Cyrlas its languagesr_Cyrl, Serbian (Cyrillic script), which is a different language there fromsr, Serbian. In each of the 21 components the Serbian translation goes, since its file is gone, and Serbian (Cyrillic script) is made from the file. Nothing is lost by that: Serbian has no suggestion, no comment, no pending change and no edit by a person in Weblate. Its address changes from/sr/to/sr_Cyrl/. A project language alias (sr_Cyrl:sr) would keep the old name; the new one says what the catalogue is. No other directory changed name, andfr_FRis read asfras before.To bring Weblate level, in this order:
postuloandplugin-paperlesscomponents to upstream (Repository maintenance → Reset), which discards those two commits.postulo. That is when Serbian changes over.After that, whatever this issue decides about the header and about how translations come back is what stops it happening again at the next translation.
Done on 2026-10-01, at the maintainer's word: Weblate is level with every repository. The three steps of the comment above, and one more.
e7539cce0here ("Token da API" for API token in pt-PT, with the three header lines Weblate wrote and the catalogue tool keeps), and postulo-paperless866be38for its header-only commit. pt-BR is as it was; the commit in Weblate did not touch it.postuloandplugin-paperlesswere reset to upstream, through the API, which discarded those two commits now thatmainholds what they said.plugin-gdpr(Plugin: Data protection) andplugin-webhook, linked topostuloand configured as the other fifteen are. The tree has seventeen plugin sets and Weblate had fifteen; that is one line of this issue's body, done.What Weblate holds now, read back through the API: 23 components, each at its repository's head with nothing to merge, push or commit and no merge failure; 69 languages in each; 3,065 strings in
postulo. Serbian is Serbian (Cyrillic script),sr_Cyrl, in all 23, and plain Serbian is gone from them.fr_FRis still read as French andpt_PT,pt_BRas before: a directory name is gettext's and Weblate reads it its own way, so nothing about postulo/postulo#337 needed setting there.What has not changed, and will put it behind again:
AUTO_UPDATEis off and no repository has a webhook, so Weblate fetches every night and merges nothing. It is level as ofe7539cce0and each plugin's head, and drifts from the next commit on.Found beside it, not repaired: the project's
glossarycomponent has no git repository under it (/app/data/vcs/postulo/glossaryholds the.tbxfiles and no.git). It has been locked with a merge failure since 2026-09-17, a task fails on it after every update, and it holds no term in any language. It still lists plain Serbian. Weblate's ownLocalRepository.create_blank_repositoryon that directory, then adding the files, would give it one.The maintainer's list of 2026-10-02 states a rule that depends on this round trip: all further translations must be made using Weblate.
#706 writes the rule into
docs/TRANSLATING.md,CONTRIBUTING.mdandCLAUDE.md.docs/TRANSLATING.md:181-190still tells a speaker to edit the.pofile and open a pull request, and that path cannot be dropped until a translation made in Weblate arrives as a pull request that passes CI, with who may sign in and under what licence settled. #706 is blocked on this issue for those parts.#705 (“Kind” or “Type”) renames message ids.
extractdrops what the source lost (tests/test_messages_tool.py:100), so a translation held only in Weblate at that moment would be lost.From the list of improvements of 2026-10-02.
Done. Weblate follows main through a Forgejo webhook, drafts new strings with LibreTranslate (needs editing =
fuzzy, compiled unless it would fail to format), and sends what it holds back as a pull request from theweblatebranch, opened by the Forgejo account weblate. The catalogues are in Weblate's own layout, so its pull requests passextract --checkunchanged (ae0c6998a, tests/test_po_roundtrip.py); CI runs the guard and holds Weblate's pull request to fr-FR and pt-PT complete (e014152fc); every catalogue set has a component (scripts/weblate.py components). Proven end to end by #829, merged as8c3d03570with every CI job green. Plugins and extensions: their own issues.