fix(marketing): serve the request's language, and make regressing this a blocked deploy
The failure mode
A production page review (152 screenshots, en/ja/ru/tr) found 25 marketing pages serving English heroes or whole English bodies to non-English visitors, each with a React #418 hydration mismatch. Root cause: every route entry called bare initServerI18n(), which activates the default locale regardless of Accept-Language — the entire RSC pass rendered English, and client components re-localized on hydration (masking it on some pages, mismatching on all). Sentry saw none of it: recoverable hydration errors aren't captured, so this class was invisible to monitoring, and status-code smoke tests can't see it either.
Point fix
initServerI18n() in marketing now resolves Accept-Language per request (quality-ordered), preloads that catalog, activates it, and returns the locale; all 31 route entries await it; the layout writes the resolved locale into <html lang> (was hardcoded "en"). Pages are already dynamic (session-aware nav), so no rendering-cost change.
Runtime proof (local build): ja request → lang="ja", 1,501 CJK chars, hero どのチームにも分かりやすい料金; en request unchanged; only remaining English is by-design SEO <meta description>.
Systematic guards — the "this must never ship again" part
- Build-time: the RSC seeding ratchet now requires the awaited request-aware call. A bare or un-awaited
initServerI18n()— the exact bug — failsbun test packages/i18n. Proven by reverting one call site: 3 pass / 1 fail naming the file. - Deploy-time:
smoke-routes.ts --localized(wired into the marketing deploy step) fetches every route withAccept-Language: jaand fails on servedlang≠"ja"or a page with no Japanese text — both read the raw served HTML, so client-side patch-up cannot mask a server regression. Run against production today it fails on every page, including ones the browser review had passed.
Follow-ups noted, not in this PR
- Capture React recoverable/hydration errors into Sentry (the monitoring blind spot)
app.superset.sh/cli/auth/coderenders its error string pre-catalog-load (client-side timing, all locales)- CSP on app.superset.sh blocks the Cloudflare Insights beacon on every page
https://claude.ai/code/session_012U97YWVsDvZwQkBaSGEmor
Summary by cubic
Marketing pages now render in the request's language instead of always English. The old initServerI18n() activated the default locale regardless of Accept-Language, so 25 pages sent English heroes or whole English bodies to ja/ru/tr visitors, and every one produced a React hydration mismatch that Sentry never captured because recoverable hydration errors aren't reported.
Bug Fixes
- The marketing
initServerI18n()now readsAccept-Languageper request, preloads and activates that catalog, and returns the locale; all 31 route entriesawaitit. - The layout writes the resolved locale into
<html lang>instead of hardcoded"en". - No rendering-cost change — pages are already dynamically rendered for the session-aware nav, and the only remaining English in a ja page is the by-design SEO meta description.
Regression Guards
- The RSC seeding test now rejects a bare or un-awaited
initServerI18n()call; reverting one call site fails the build. smoke-routes.ts --localized, wired into the marketing deploy step, fetches every route withAccept-Language: jaand fails when the served HTML haslang="en"or no Japanese text, so client-side patch-up can't mask a server regression.
Summary by CodeRabbit
-
New Features
- Marketing pages now serve content in the language requested by the browser.
- The page language attribute reflects the selected locale.
-
Bug Fixes
- Improved localized rendering by ensuring translations load before page content appears.
- Enhanced localization consistency across marketing, blog, legal, marketplace, and 404 pages.
-
Tests
- Added smoke-test coverage to verify Japanese localization and translated page content across routes.