- Shipped
- August 26, 2026 at 1:26 AM UTC
- Author
- kamo
- Commit
- c8d0f21
Batch 3 of the client-side performance audit. The root layout passed every message to NextIntlClientProvider on every route. The catalogue is 1.05 MB compact — 1.16 MB once RSC inlines it as an escaped string literal, 328 KB gzip / 250 KB brotli — plus ~16 ms of JSON.parse on a desktop CPU and roughly 95 ms at PageSpeed's 6x throttle, before any of it is used. /validate and /logout need two strings of it. AuthedChrome returns children bare on both, none of the shell components mounted above it translate anything, and the only translated text on screen is app/loading.tsx's two `loading.*` keys — verified by scanning every file that renders on those paths. Every sign-in passes through /validate, so the handoff was carrying 1.16 MB of document for two strings. A server component cannot see its own path — headers() carries what the client sent, and the client sends no pathname — so proxy.ts now stamps x-kamo-pathname on the two return sites that serve documents, and the layout reads it from the headers() call it was already making. The fallback direction is the part that matters. When the header is absent the layout sends the FULL catalogue, never an empty slice. A missing namespace does not throw and does not warn: IntlErrorHandler suppresses MISSING_MESSAGE and its getMessageFallback returns the key, so the member would simply see `loading.preparingWorkspace` where prose belongs. Defaulting to everything means a matcher change or a new deployment shape degrades to exactly today's behaviour instead of to dotted keys on the one page every sign-in passes through. Adds a test for the same reason. It scans the pre-session pages for literal `t('ns.…')` namespaces and fails if any falls outside the slice, so adding a translated string to the sign-in handoff is caught at test time rather than seen by a member. Confirmed non-vacuous: the scanner finds the `loading` namespace it should, and injecting a foreign namespace into loading.tsx makes it fail. This is deliberately the narrow version. Slicing per-route across all 143 routes is worth several times more, but it needs an AST walk over every useTranslations call site and a build guard against non-literal namespace arguments — the same silent failure mode, spread across the whole product. Not something to land alongside anything else. Verified: tsc --noEmit clean over every file in this change; all 8 guard scripts pass; the new test passes and fails correctly under a negative control.