Report the stale keys that are stale, and remove them #329

Closed
opened 2026-09-10 23:00:15 +00:00 by jcoffey-dev · 0 comments
Owner

The restored check reported 41 stale keys per catalogue. Ten of them were.

Why 31 were false

Strings held in a constant and translated where they render — t(b.description), t(group), t(c.label) — reach t() as a variable, so there is no literal at the call site for a static scan to find.

The script already chased two of those shapes (label: properties, objects named *_LABELS) with a comment about having cried wolf 33 times. The shapes kept coming:

Reported stale Actually lives in
Go to Inbox, Archive and next, Send message, … description: on keyboard bindings, via t(b.description)
Actions, Navigation, Conversation group: on the same bindings
Day view, Week view, Month view, Agenda view the calendar's view table
the five read-receipt refusals the MDN reason map
Classic palette.ts

Deleting any of them would have un-translated that text in nine languages. The folder names were a near miss of the same kind — tc("folder", "Inbox") builds a contextual key that appears nowhere as one literal — and only survived because the script already had a tc() case.

The fix

Chasing shapes one at a time is the wrong shape of fix. Stale detection now asks only "is this key still written down anywhere in the source" — any string literal counts.

That under-reports, deliberately. A missed stale key costs one line of dead translation; a false one costs the credibility of the check and every real finding after it. Which is exactly what happened — a report that was three-quarters noise is one nobody acts on, and these sat unread long enough to need their own commit.

Two sets, because there are two questions. wanted is what a catalogue owes and stays strict — coverage is measured against it, and widening it counted every CSS class and JMAP method name as an untranslated string (it read 29% while the two shared a set). seen is every literal, and answers only whether a key is still written down.

What was actually dead

Removed from all nine catalogues:

  • Availability on {date}, Import vCard, PDF
  • two settings hints, both replaced by rewordings that are still live
  • the Catppuccin palette description
  • Tuesday, Wednesday, Thursday, Friday — left behind when the week-start dropdown narrowed to Monday/Saturday/Sunday, and appearing since only in comments

Checks

  • npm run i18n:checkno stale section at all, nine locales clean, exit 0
  • coverage unchanged at 1269/1285 (99%) — none of the ten was ever owed, so removing them moves nothing
  • npm run typecheck — clean, both packages
  • npm test — 1168 web tests, 168 server tests, all passing
  • npm run build — clean

The report also prints the whole list now rather than the first 25. With the false positives gone it is short, and a truncated list of dead keys is one somebody has to run twice to act on.

Merged 2026-09-10 as coffey-labs/ihasmail@c3d2dc2418

Rebuilt from: GH Archive, git history, session transcript.

The restored check reported **41 stale keys** per catalogue. **Ten of them were.** ## Why 31 were false Strings held in a constant and translated where they render — `t(b.description)`, `t(group)`, `t(c.label)` — reach `t()` as a *variable*, so there is no literal at the call site for a static scan to find. The script already chased two of those shapes (`label:` properties, objects named `*_LABELS`) with a comment about having cried wolf 33 times. The shapes kept coming: | Reported stale | Actually lives in | |---|---| | `Go to Inbox`, `Archive and next`, `Send message`, … | `description:` on keyboard bindings, via `t(b.description)` | | `Actions`, `Navigation`, `Conversation` | `group:` on the same bindings | | `Day view`, `Week view`, `Month view`, `Agenda view` | the calendar's view table | | the five read-receipt refusals | the MDN reason map | | `Classic` | `palette.ts` | Deleting any of them would have un-translated that text in nine languages. The folder names were a near miss of the same kind — `tc("folder", "Inbox")` builds a contextual key that appears nowhere as one literal — and only survived because the script already had a `tc()` case. ## The fix Chasing shapes one at a time is the wrong shape of fix. Stale detection now asks only **"is this key still written down anywhere in the source"** — any string literal counts. That under-reports, deliberately. A missed stale key costs one line of dead translation; a false one costs the credibility of the check and every real finding after it. Which is exactly what happened — a report that was three-quarters noise is one nobody acts on, and these sat unread long enough to need their own commit. **Two sets, because there are two questions.** `wanted` is what a catalogue *owes* and stays strict — coverage is measured against it, and widening it counted every CSS class and JMAP method name as an untranslated string (it read **29%** while the two shared a set). `seen` is every literal, and answers only whether a key is still written down. ## What was actually dead Removed from all nine catalogues: - `Availability on {date}`, `Import vCard`, `PDF` - two settings hints, both replaced by rewordings that are still live - the Catppuccin palette description - `Tuesday`, `Wednesday`, `Thursday`, `Friday` — left behind when the week-start dropdown narrowed to Monday/Saturday/Sunday, and appearing since only in comments ## Checks - `npm run i18n:check` — **no stale section at all**, nine locales clean, exit 0 - coverage unchanged at **1269/1285 (99%)** — none of the ten was ever owed, so removing them moves nothing - `npm run typecheck` — clean, both packages - `npm test` — 1168 web tests, 168 server tests, all passing - `npm run build` — clean The report also prints the whole list now rather than the first 25. With the false positives gone it is short, and a truncated list of dead keys is one somebody has to run twice to act on. **Merged** 2026-09-10 as coffey-labs/ihasmail@c3d2dc241819 <sub>Rebuilt from: GH Archive, git history, session transcript.</sub>
This repo is archived. You cannot comment on issues.