Skip to content

[qt] Add Qt ID-based TS formatter with plural support#28

Merged
biodranik merged 1 commit into
organicmapsfrom
qt
May 21, 2026
Merged

[qt] Add Qt ID-based TS formatter with plural support#28
biodranik merged 1 commit into
organicmapsfrom
qt

Conversation

@biodranik

@biodranik biodranik commented May 21, 2026

Copy link
Copy Markdown
Member

Summary

Adds a first-class qt formatter that emits Qt Linguist TS files (consumed by lrelease → QM bundles, looked up at runtime via qtTrId).

  • Output: ID-based <message id="..."> in a single empty <context><name></name> (required by qtTrId); definition comments emitted as <extracomment>; sequential %@/%d%1,%2 placeholder conversion with numbered %N$x preserved; leading/trailing whitespace on each line (space, tab, NBSP, …) escaped as numeric entities so it survives lrelease.
  • Plurals: Twine's CLDR-named categories → Qt's positional <numerusform> via a table derived from qttools/src/linguist/shared/numerus.cpp. Per-form fallback chain translated[cat] → translated[other] → developer[cat] → developer[other]. Region tags fall back to base language; pt-BR, zh-Hans/zh-Hant, ast get explicit entries.
  • Read support covers non-plural messages; numerus forms are skipped (positional → CLDR is lossy, no current pipeline needs it).
  • Fixes a pre-existing crash in OutputProcessor.process() where a plural definition whose target language had only a non-plural translation (or no fallback language with plural data) raised KeyError/TypeError.

Test plan

  • pytest python_twine/tests — 132 passing (was 119; +13 new tests covering formatter singular/plural paths, fallback chain, escaping, extracomment, OutputProcessor regression, and lrelease integration).
  • Regenerated all 63 OM language files from data/strings/strings.txt; every file compiles cleanly through lrelease with no warnings or errors.
  • Spot-checked plural keys (bookmarks_places, tracks, bookmarks_detect_message) — now emit correct positional <numerusform> per language instead of being silently rendered as their other form.
  • Verified <extracomment> placement matches Qt Linguist's expectations (between <message ...> and <source>).

LLM tooling was used to draft and refine portions of this change.

CC @Osyotr

Adds a first-class `qt` formatter that emits Qt Linguist TS files
(consumed by lrelease → QM bundles, looked up at runtime via qtTrId).

Output shape: ID-based <message id="..."> messages wrapped in a
single empty <context><name></name> block (required by qtTrId).
Definitions' comments are emitted as <extracomment> for translator
context in Qt Linguist. Sequential %@/%d → %1, %2 placeholder
conversion; numbered %N$x preserved as %N; any leading/trailing
whitespace on each line (space, tab, NBSP, …) is escaped as a
numeric entity so it survives lrelease.

Plural (numerus) support: maps Twine's CLDR-named plural categories
(one/few/many/zero/two/other) to Qt's positional <numerusform>
entries via a table derived from Qt's
qttools/src/linguist/shared/numerus.cpp. The first count-eligible
placeholder in each form becomes %n; extras stay as %1, %2.

Per-form fallback chain when a category is missing for the target
language: translated[cat] → translated['other'] → developer[cat] →
developer['other'] → fail. Languages absent from the Qt plural
table fail generation (no silent default), with region tags
falling back to base language (es-MX → es). pt-BR and
zh-Hans/zh-Hant get explicit entries.

Read support covers non-plural messages; numerus forms are skipped
on read since the positional-to-CLDR mapping is lossy and no
current pipeline feeds Qt TS back into Twine.

Asturian (ast) is not in Qt's numerusTable but follows CLDR
english-style 2-form rule; added explicitly.

Also fixes a pre-existing crash in OutputProcessor.process(): a
plural definition whose target language had only a non-plural
translation (or no fallback language with plural data) used to
raise KeyError/TypeError. The processor now leaves the plural slot
absent in those cases and the formatter falls through to the
non-plural path.

Tested against OM's strings.txt: all 63 language files compile
cleanly via lrelease, including the three real plural keys
(bookmarks_places, tracks, bookmarks_detect_message) which now
emit correct numerus messages instead of being silently rendered
as their 'other' form. New pytest suites cover the formatter
(singular + plural, fallbacks, escaping, extracomment) and an
lrelease compilation check as a skip-if-missing integration test.

Signed-off-by: Alexander Borsuk <me@alex.bio>
@biodranik biodranik merged commit 9b0137b into organicmaps May 21, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants