diff --git a/docs/i18n.md b/docs/i18n.md index eb4fb5c3ee..b1e60a91b3 100644 --- a/docs/i18n.md +++ b/docs/i18n.md @@ -296,6 +296,23 @@ trusting the count. Install/update flows and any maintenance action are the toast-heavy cases. A count from `pnpm run lint:i18n` is a floor on the visible half, never a work-list. +**Default values in a destructuring pattern are a third invisible shape, and the +fix is not the obvious one.** `ActionButtonContent` carried three: + +```tsx +export function ActionButtonContent({ + pendingLabel = "Running...", // NOT flagged — not a literal in JSX + successLabel = "Done", + errorLabel = "Failed", +}: Props) { +``` + +These are evaluated in the parameter list, which runs *before* the component +body, so a `const { t } = useTranslation()` in the body is not in scope for them — +you cannot fix this in place. Resolve the fallback inside the body instead +(`const pending = pendingLabel ?? t("system:jobRunning")`). Prop defaults on a +shared presentational component are a common home for this. + `externalize-strings.mjs` covers those shapes — it walks `.ts` as well as `.tsx` and handles template literals and dialog/toast arguments — but **a zero from `pnpm lint` does not by @@ -490,6 +507,50 @@ While the migration is in flight, an unmigrated screen is expected to be full of plain English — pseudo only proves anything about screens whose components are on the `i18nGuardFiles` allowlist. +### Walk it by consumer, not by route + +Which screens you walk matters as much as what you look for. A component's copy +is verified by walking **every route that mounts it** — not the routes your +migration owns. + +The natural method fails here. Walking the import closure *from your own pages* +traverses the graph in the wrong direction: it finds what your routes render, and +by construction never finds a route that renders *you*. One migration hit it in +both directions — the cards it converted mount only on a sibling migration's +route, and a card that sibling owned mounts on its own. Neither could have been +verified from its own pages alone. + +`job-progress-indicator.tsx` is the worked example. It reported **0** findings, +because its `Queued` / `Running` / `Done` / `Failed` come out of a plain `switch` +in a non-JSX function, and it has four consumers spanning two directories +(`database-stats-card`, `disk-usage-card`, `backups-table`, and Storage's +`storage-quarantine-card`). Three separate migrations rendered it, and each left +it — a component every one of them touched, and none of them owned. The System +migration finally took it and walked Storage's already-merged route to confirm. + +Per component you touch: + +```bash +grep -rn '