Skip to content

F182 B1: i18n sweep — shared chrome + primitives (nav labelKey, AppLayout, dialogs, PWA)#238

Merged
mforce merged 4 commits into
mainfrom
f182-i18n-b1-chrome
Jul 27, 2026
Merged

F182 B1: i18n sweep — shared chrome + primitives (nav labelKey, AppLayout, dialogs, PWA)#238
mforce merged 4 commits into
mainfrom
f182-i18n-b1-chrome

Conversation

@mforce

@mforce mforce commented Jul 27, 2026

Copy link
Copy Markdown
Owner

B1 — shared chrome + primitive components. Second batch of the #182 string sweep (foundation B0 merged in #237). Externalizes the app's navigation chrome and shared primitives to the catalog, English-first — no es/tl for these (they fall back to English until the scheduled native-review pass; only the already-translated set + Help carry translations).

Base main. Behavior-preserving; every string renders the same English. i18n:scan on src/routes+src/components: 843 → 827.

What this externalizes

  • nav.tsx → typed labelKey model. navGroups/tabEntries are pure functions (can't call a hook), so nav items now carry a typed labelKey (keyof the nav catalog → a typo is a compile error) instead of a hardcoded English label; AppLayout, BottomNav, and the More sheet translate at render. One label source for sidebar/tabs/mobile.
  • AppLayout — group headings, landmark aria-labels, sign-out; plus a skip-to-content link and a catalog-composed document.title (per-page label + suffix) that didn't exist before (both were plan-review "missed sources" — document.title, a11y).
  • BottomNav — tab + More/Menu labels (new BottomNav.test.tsx).
  • Primitives — Dialog (common.close), NumberField (interpolated stepper aria-labels), ErrorBoundary (fallback heading/body/reload), ThemeToggle, useConfirm (reuses common.confirm/cancel), and the PWA UpdatePrompt banner.

Namespaces (all English-only)

nav, numberField, errorBoundary, themeToggle, useConfirm, pwa — added to en.ts only; not in TRANSLATED_NAMESPACES, so es.ts/tl.ts are byte-unchanged and parity stays green (the allowlist guard fails the build if a deferred namespace is ever drafted into es/tl). Shared verbs reuse common atoms; no duplication.

Testing / review

Wiring tests use the B0 i18n.addResource override technique — each asserts the overridden catalog value renders (marker present + original literal absent), so a still-hardcoded string fails (empirically confirmed by a reverted mutation test). npm run test 786 green; coverage above the (unchanged) floors — no locked path touched; npm run typecheck clean; npm run build succeeds.

Reviewed per policy: each of the 4 tasks was individually reviewed (spec + quality, several with live mutation probes), then a whole-branch review (Claude comprehensive lens — READY; codex's whole-branch run produced no usable verdict). One Minor (the document.title effect's dependency array churns harmlessly every render) is left as an optional follow-up.

Not in scope

es/tl for these screens (deferred native-review pass, tracked in #182); the remaining screens (B2–B6).

mforce added 4 commits July 26, 2026 20:53
…182)

First screen-externalization batch (B1, Task 7): navGroups()/tabEntries()
are pure functions, so nav items now carry a typed labelKey into the new
English-only `nav` namespace instead of a hardcoded label; AppLayout and
BottomNav translate it at their render sites — one label source for the
sidebar, the bottom tab bar, and the More sheet.

Also externalizes AppLayout's landmark aria-labels, sign-out, the
farm-load-failed banners + retry button, adds a skip-to-content link, and
composes document.title from the active nav entry's label + a shared
suffix. BottomNav gets its first dedicated test file.

`nav` stays out of TRANSLATED_NAMESPACES (English-first); es/tl untouched.
i18n:scan (src/routes src/components) count: 843 -> 835.
Batch B1, Task 8 of the i18n string sweep. Dialog's close button reuses
common.close; NumberField's Increase/Decrease aria-labels and
ErrorBoundary's crash-fallback copy get their own English-only
namespaces (numberField, errorBoundary), following the same
not-yet-translated treatment Task 7 gave `nav`. Wiring tests use the
i18n.addResource override technique to prove the catalog is actually
read, not just falling back to hardcoded English.
…182)

Adds themeToggle/useConfirm/pwa namespaces (English-only, like nav and
numberField from Task 7/8) and wires each component to t(). useConfirm's
Cancel button reuses the existing common.cancel atom instead of a new key.
…talog-sourced (#182)

Two deferred review findings from the B1 chrome/primitives batch (Tasks
7-9): the nav.help comment claimed the key was reused for both the Help
destination and the Help group heading, but nav.tsx actually uses two
distinct keys (groupHelp for the heading, help for the destination),
matching every other group's group*/entry-key pairing — reword to say
so. Also add a wiring test proving AppLayout's document.title reads its
per-page label from the catalog: the existing test only asserted the
titleSuffix half via an addResource override, so a hardcoded label
would have passed the label half under default lng:"en".
@mforce
mforce merged commit fc0f834 into main Jul 27, 2026
7 checks passed
@mforce
mforce deleted the f182-i18n-b1-chrome branch July 27, 2026 07:06
mforce added a commit that referenced this pull request Jul 27, 2026
…) — stacked on #238 (#239)

* feat(web): externalize shared nav chrome — nav, AppLayout, BottomNav (#182)

First screen-externalization batch (B1, Task 7): navGroups()/tabEntries()
are pure functions, so nav items now carry a typed labelKey into the new
English-only `nav` namespace instead of a hardcoded label; AppLayout and
BottomNav translate it at their render sites — one label source for the
sidebar, the bottom tab bar, and the More sheet.

Also externalizes AppLayout's landmark aria-labels, sign-out, the
farm-load-failed banners + retry button, adds a skip-to-content link, and
composes document.title from the active nav entry's label + a shared
suffix. BottomNav gets its first dedicated test file.

`nav` stays out of TRANSLATED_NAMESPACES (English-first); es/tl untouched.
i18n:scan (src/routes src/components) count: 843 -> 835.

* feat(web): externalize Dialog, NumberField, ErrorBoundary strings (#182)

Batch B1, Task 8 of the i18n string sweep. Dialog's close button reuses
common.close; NumberField's Increase/Decrease aria-labels and
ErrorBoundary's crash-fallback copy get their own English-only
namespaces (numberField, errorBoundary), following the same
not-yet-translated treatment Task 7 gave `nav`. Wiring tests use the
i18n.addResource override technique to prove the catalog is actually
read, not just falling back to hardcoded English.

* feat(web): externalize ThemeToggle, useConfirm, UpdatePrompt strings (#182)

Adds themeToggle/useConfirm/pwa namespaces (English-only, like nav and
numberField from Task 7/8) and wires each component to t(). useConfirm's
Cancel button reuses the existing common.cancel atom instead of a new key.

* fix(web): correct nav.help comment + prove per-page title label is catalog-sourced (#182)

Two deferred review findings from the B1 chrome/primitives batch (Tasks
7-9): the nav.help comment claimed the key was reused for both the Help
destination and the Help group heading, but nav.tsx actually uses two
distinct keys (groupHelp for the heading, help for the destination),
matching every other group's group*/entry-key pairing — reword to say
so. Also add a wiring test proving AppLayout's document.title reads its
per-page label from the catalog: the existing test only asserted the
titleSuffix half via an addResource override, so a hardcoded label
would have passed the label half under default lng:"en".

* feat(web): externalize DailyEntryPage copy to the i18n catalog (#182)

Task 11 (batch B2): every user-facing string in DailyEntryPage now flows
through a new `dailyEntry` namespace (English-first, en.ts only). The
entry-locked banner's status word is wired through enums.statusLabel,
which harmonizes ManagerAdjusted's visible text from raw "manageradjusted"
to "Adjusted" (matching HistoryPage's own bespoke badge).

* feat(web): externalize Dashboard.tsx strings to the i18n catalog

Adds a new English-first `dashboard` namespace (en.ts only, not in
TRANSLATED_NAMESPACES) covering the landing page's headings, stat
labels, empty-state copy, and table headers. Wires both StatusBadge
render sites (today's per-flock entry status, recent order status)
through the B0 enums `statusLabel` helper, which harmonizes a
ManagerAdjusted entry from raw "ManagerAdjusted" to "Adjusted",
matching HistoryPage's existing badge for the same state. Also
removes a stray blank line left after the function signature.

(#182)

* feat(web): externalize WaterPage strings (#182)

Externalizes WaterPage.tsx to a new `water` namespace (en.ts only,
English-first), wiring the Source and Unit displays through the B0
waterSourceLabel/waterUnitLabel enum helpers everywhere they render
(select options, table cells, and the quantity label's unit suffix).
Both families are identity-labelled in English, so nothing renders
differently. Adds i18n.addResource wiring tests alongside the existing
suite, which stays green untouched since the English text is
unchanged.

* feat(web): externalize GradesPage strings (#182)

Externalizes GradesPage.tsx to a new English-first `grades` namespace
(en.ts only, not in TRANSLATED_NAMESPACES) — form labels, table
headers, buttons, the intro paragraph, load-failure message, and the
saleable "yes" badge. The grade-type select/table-cell now render
through the B0 gradeTypeLabel enum helper instead of the raw wire
value; grade NAMES (g.name) stay raw as free-form farm data. Adds
i18n.addResource wiring tests confirming the screen reads the catalog
(and the enums catalog for grade type) rather than hardcoded literals.

* fix(web): wire GradesPage StatusBadge through statusLabel (#182)

Review fix round 1 on Task 14: the Active/Inactive StatusBadge cell was
left unwired, inconsistent with sibling B2 screen Dashboard (Task 12),
which wires its bare StatusBadge sites through statusLabel. Adds
label={statusLabel(...)} alongside the existing raw status= (kept for
the variant class) — 100% text-preserving today (identity labels) and
future-proofs the badge for the eventual status translation pass.

---------

Co-authored-by: mforce <>
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.

1 participant