diff options
Diffstat (limited to 'docs/superpowers/plans')
| -rw-r--r-- | docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md | 94 | ||||
| -rw-r--r-- | docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md | 44 |
2 files changed, 95 insertions, 43 deletions
diff --git a/docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md b/docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md index e6c7840..1704a28 100644 --- a/docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md +++ b/docs/superpowers/plans/2026-08-03-post-0.1.0-usability-closed.md @@ -5196,3 +5196,97 @@ The test asserts the Run item exists, that it is first, that triggering it reaches the query, and that the edit actions survived beside it. Restoring the old wiring fails it on the first of those and names the Qt behaviour rather than reporting a wrong query string. + +## 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. + +**Done 2026-08-15, unreleased.** Both halves shipped: the audit and the wiring, +plus an Italian translation of all 355 strings. See +`specs/2026-08-15-i18n-design.md`. + +**What the audit found, and it was not what this entry predicted.** The `tr()` +discipline was largely holding: `lupdate` extracted 327 strings across 13 +contexts before any change. The real find was eight strings that could never be +translated in any language. `kFields[]` in `src/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 entire vocabulary of the rule +builder, invisible to every translation file that could ever exist. The source +compiles, reads correctly, and only `lupdate` reveals it, exactly as this +entry's verification note said. + +**The fix this entry recommended is the wrong one, measured.** The entry says a +string in a free function needs `Q_DECLARE_TR_FUNCTIONS`, which is true for the +case `querycompleter.cpp` solves and false here: a class carrying that macro +beside the array still extracts 0 strings, because `lupdate` needs the context +attached to the LITERAL, not to a neighbouring class. `QT_TRANSLATE_NOOP("TagRulesDialog", ...)` +is what works, verified extracting 8 of 8, and it names the context the reading +`tr()` already resolves against so the use site needed no change. + +**Twenty warnings were not translatable either**, seventeen in `config.cpp` and +three in `keymap.cpp`, all user-facing through the status label and the +"Configuration problems" modal. `Config` already had the macro; `KeyMap` needed +it. + +**`main.cpp`'s `--help` and `--version` stay bare `printf` deliberately.** They +run before `QApplication` exists, so no translator could serve them. + +**Wiring.** A `QTranslator` on `main`'s stack, which is load-bearing: one scoped +to a helper unloads on return and silently reverts every string. Installed +before `Config` loads, since config warnings are generated at load time and are +among the translated strings. `QLocale()` reads the environment, so +`LANG=it_IT.UTF-8` selects the file with no config key of this project's own, +and a missing `.qm` returns false and runs English. `LinguistTools` is optional +in CMake: a build without it is English-only rather than broken. + +**Verified**, per this entry's own standard that `lupdate` output is the +evidence: 355 strings extracted with zero context warnings (was 327 with eight), +`lrelease` reporting 355 finished and 0 unfinished, and the built `.qm` loaded +in a standalone probe printing `From -> Da`, `&Archive -> &Archivia`, and both +Italian plural forms. `tests/test_translations.cpp` guards it and was mutation +checked twice: it fails on an emptied translation, and it fails naming the +defect when `QT_TRANSLATE_NOOP` is reverted to `QT_TR_NOOP`. + +**Not done:** no language-selection UI, and no second language. `QLocale()` +reading the environment is how a Linux desktop already chooses; add a selector +when there is something to choose between. 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 |
