diff options
| author | Danilo M. <danix@danix.xyz> | 2026-08-15 12:37:41 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-08-15 12:37:41 +0200 |
| commit | 5a3f827a01d1902a0dadc5debb4200138d4af885 (patch) | |
| tree | 5b00207aefccf625a54ac4b928a05561d1a0fff1 /docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md | |
| parent | 8e7208dc771bc9344f583442ad58cdd5b4178233 (diff) | |
| download | qtmaildir-5a3f827a01d1902a0dadc5debb4200138d4af885.tar.gz qtmaildir-5a3f827a01d1902a0dadc5debb4200138d4af885.zip | |
feat(i18n): wire translations and ship an Italian one (item 22)
Nothing loaded a translation before this: no QTranslator, no .ts file and
no build rule, so every string was English whatever the locale said. The
language now comes from the environment, LANG=it_IT.UTF-8, and any other
locale runs in English as before.
The audit found that the tr() discipline was largely holding, and found
eight strings that could never be translated into any language. kFields[]
in tagrulesdialog.cpp declared the rule-builder field labels with
QT_TR_NOOP inside an anonymous namespace, where lupdate reports "tr()
cannot be called without context" and extracts nothing, while the use site
calls TagRulesDialog::tr() on them at runtime. From, To, Cc, Subject, Tag,
Folder, Attachment and Date: the whole vocabulary of the rule builder,
absent from every translation file that could ever exist. The source
compiles and reads correctly; only lupdate reveals it.
Q_DECLARE_TR_FUNCTIONS is not the fix for that case, though it is the fix
for a free function calling tr(). Measured against lupdate: a class
carrying the macro beside the array still extracts 0 strings, because the
context must be attached to the literal itself. QT_TRANSLATE_NOOP names it
explicitly and matches the tr() that already reads them, so the use site
needed no change.
Twenty configuration and keybinding warnings were not translatable either.
They are user-facing, reaching the status label and the "Configuration
problems" dialog. Config already had the tr() macro; KeyMap needed it.
Translating the filter labels then broke startup_query, found in hand
testing: a filter's name is a translated label, so `startup_query = Inbox`
matched nothing where the filter shows as "In arrivo". The application
opened a different view and reported the user's own working config as
invalid. Resolution matches the generator as well now, which is stored in
queries.json and identical in every locale; the translated name still
works. The regression test installs a real QTranslator rather than a stub,
since the bug lives in the gap between the stored string and the displayed
one, and it writes a queries.json because the warning it asserts on is
guarded by a non-empty saved-query list: without one the branch never runs
and the test passes against a broken check.
main.cpp's --help and --version stay bare printf, as they run before
QApplication exists and no translator could serve them.
Verified per the backlog's own standard, that lupdate output is the
evidence rather than reading: 355 strings extracted with zero context
warnings, where before there were 327 with eight; lrelease reporting 355
finished and 0 unfinished; the built .qm loaded in a standalone probe
printing "From -> Da" and both Italian plural forms; and the install rule
placing it where main.cpp looks. test_translations guards it and was
mutation checked, failing on an emptied translation and naming the defect
when QT_TRANSLATE_NOOP is reverted.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md')
| -rw-r--r-- | docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md | 44 |
1 files changed, 1 insertions, 43 deletions
diff --git a/docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md b/docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md index c02de31..e921e89 100644 --- a/docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md +++ b/docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md @@ -86,7 +86,7 @@ taking that too literally. | 19 | No prompt to sync on exit when edits are pending | behavior | S | **done** | | 20 | Thread view does not match the user's mental model | presentation | L | **done** 2026-08-10, as the card list; see 53 | | 21 | Default shortcuts are not sensible enough | discoverability | S | open | -| 22 | Translatability audit and i18n wiring | correctness | M | open | +| 22 | Translatability audit and i18n wiring | correctness | M | **done** 2026-08-15, unreleased; see `specs/2026-08-15-i18n-design.md`. Found eight rule-builder labels that could never be translated in any language, and twenty untranslatable warnings. Ships an Italian translation of all 355 strings | | 23 | No way to save a search query from the UI | workflow | M | **done** 2026-08-13, shipped in 0.18.0; see `specs/2026-08-13-saved-queries-design.md` | | 24 | No right-click actions on the thread list | discoverability | S | **done** | | 25 | No select-all, and bulk actions are undiscoverable | workflow | S | **done** | @@ -194,48 +194,6 @@ that appeared to be bound. See `KeyMap::defaultBindings()` and the query bar claims it back while focused, so a proposal that moves it must not resurrect that bug. -## 22. Translatability audit and i18n wiring - -**Observed (user, 2026-08-04):** "a full check of the codebase and wiring up of -the i18n system." - -**This is a debt `CLAUDE.md` already records.** The rule that every user-facing -string must be wrapped in `tr()` was added while building query completion, and -that file states plainly that "pre-existing code has not been audited against -this rule". This item is that audit, plus the loading machinery which does not -exist at all. - -**Two halves, and they are different sizes.** - -- *The audit.* Every user-visible string in `src/` checked for `tr()`, with the - translation context correct: a string in a free function needs - `Q_DECLARE_TR_FUNCTIONS`, since calling `QObject::tr()` compiles but files it - under the wrong context. `lupdate` output is the evidence here, not reading. -- *The wiring.* Nothing loads a `.qm` file today: there is no `QTranslator` in - `main.cpp`, no `.ts` files in the tree, and no CMake rule to build or install - them. Until that exists, a translated string has nowhere to come from. - -**Constraint:** query syntax is not user-facing text. notmuch keywords such as -`tag:` and `date:` are wire format and must never be translated, only the prose -describing them. The completion vocabulary is exactly this trap: the values are -literal, the descriptions are prose. - -**Verification:** run `lupdate` and read the generated `.ts`. A string that -does not appear there is not translatable, whatever the source looks like. - -**No longer on demand.** The entry said to do this when a defect needs it, not -before (user, 2026-08-04). Item 66 needs it now, so the deliverable is a RED -reproduction of that defect, not fixture wiring on its own. Fixing 66 is -deliberately excluded: it has never been isolated, and designing a fix beside a -hypothesis is how a wrong one gets locked in. - -**Smaller than this entry has read since 2026-08-04.** No hook has to be added -to `MainWindow`. `wireWorker()` (`src/mainwindow.cpp:1440`) already builds the -worker from `m_config.notmuchConfig()`, an ordinary config key, so a test that -writes a `qtmaildir.conf` pointing at the fixture gets a real worker through the -shipping path with nothing in `src/` changed. Mind the `[general]` prefix trap -when writing that file. - ## 40. No live filter over the current view **Observed (user, 2026-08-05):** "search in current view", spelled out as two |
