thunderbird / thunderbird/thunderbird-android

Weblate — Add `normalize` command to weblate-cli to repair plural quantities Weblate omits

Open
#11,366 0 comments 0 reactions 1 assignee View on GitHub

@rafaeltonholo is already working on this.

Since Aug 5, 2026.

type: developer experience type: translation
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 categories table, if a <plurals> block is missing a
    category, duplicate its other value into it. No other -> fail, don't guess.
  • covers both res/values-* and composeResources/values-*

Acceptance criteria

  • weblate normalize requires 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.
  • --check exits non-zero when normalization is required.
  • A block missing other produces an error.
  • Both workflows invoke the command as described.
  • cli/weblate-cli/README.md is updated.

Implementation notes

  • project/ModuleDiscovery already classifies res/ vs composeResources/, but skips values-* dirs; needs extension or a sibling discoverer.
  • Two blockers for an offline command: WeblateCli.token is .required(), and BaseCommand.run() always builds a WeblateClient and 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 in 72525d8bad.

CI

  • translation-update.yml: apply mode between Apply Weblate changes and Create commit.
  • quality-checks.yml: --check, fails the build.

Out of scope

  • Other sync hygiene from #11360 (NBSP stripped from ~299 strings; ~644 empty strings.xml churned). Possible future normalize rules, separate tickets.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.