thunderbird / thunderbird/thunderbird-android
Weblate — Add `normalize` command to weblate-cli to repair plural quantities Weblate omits
@rafaeltonholo is already working on this.
Since Aug 5, 2026.
- Dominant language
- Kotlin
- Stars
- 14k
- Forks
- 2.8k
- Avg merge
- 3d 3h
- Merged PRs (30d)
- 57
Description
Problem
Weblate writes plural quantities per the current CLDR. Android picks a category using the device's ICU, so older devices request categories Weblate no longer writes. Hebrew is the only affected locale; the only one where CLDR removed a category (iw: CLDR41 [many, one, two] -> CLDR42 [one, two]).
Lint can't catch it: PluralsDetector picks its CLDR table from Project.getTargetSdk() (36 -> CLDR42), where many isn't relevant for iw. targetSdk only rises, so the blind spot is permanent. Result: silently regressed 4 times (a3dcbefeca, d895e4a88a, 79db47c04e), each time restored by hand.
No Weblate-side fix exists; plural definitions affect display only, not file storage; upstream #7520 is open/low-priority; hosted Weblate allows no custom addons. Then, we need to add some guards/auto-fixes in :cli:weblate-cli to address this issue
Scope
Add a local (no API token) command:
weblate normalize [--check]
- default: rewrite in place;
--check: report and exit non-zero (CI) - rule: for a declarative
locale -> required categoriestable, if a<plurals>block is missing a
category, duplicate itsothervalue into it. Noother-> fail, don't guess. - covers both
res/values-*andcomposeResources/values-*
Acceptance criteria
weblate normalizerequires no API token.- Android and Compose locale resource directories are covered.
- No content changes outside the inserted quantity lines.
- Escapes,
<xliff:g>elements, CDATA, and indentation are preserved. - Re-running produces no changes.
--checkexits non-zero when normalization is required.- A block missing
otherproduces an error. - Both workflows invoke the command as described.
cli/weblate-cli/README.mdis updated.
Implementation notes
project/ModuleDiscoveryalready classifiesres/vscomposeResources/, but skipsvalues-*dirs; needs extension or a sibling discoverer.- Two blockers for an offline command:
WeblateCli.tokenis.required(), andBaseCommand.run()always builds aWeblateClientand loads component config. Make the API wiring opt-in. - Text-preserving, not an XML round-trip — escapes (
\"),<xliff:g>, CDATA and indentation must survive byte-for-byte. Verified feasible with a text-based prototype: exactly 12 items, no other bytes touched, idempotent, byte-identical to the hand fix in72525d8bad.
CI
translation-update.yml: apply mode betweenApply Weblate changesandCreate commit.quality-checks.yml:--check, fails the build.
Out of scope
- Other sync hygiene from #11360 (NBSP stripped from ~299 strings; ~644 empty
strings.xmlchurned). Possible futurenormalizerules, separate tickets.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.