Phase-2 i18n wiring for the last & hardest app (the live-POS Next.js App Router on a Cloudflare Worker).
Verified on demo.staging 6/6 + staging smoke 5/5.
nix_cafe_lang cookie
(EN / ខ្មែរ), not a /[locale] URL segment — so the /cafe basePath, the auth middleware, and the
POS-lockdown middleware are all untouched. 1,926 strings across 97 files were converted to
t(...) calls; messages are generated from the flat localization export into nested
messages/{en,km}.json by a re-runnable script. The km bundle is English-until-translated —
every key resolves to English until Narong fills the Khmer column, then km.json regenerates with one command
(no code change). A language switcher sits in the authed top bar.
<html
lang> updates, and every authed surface renders cleanly in both locales.

EN highlighted, <html lang="en">.
<html lang="km">, KM highlighted (text falls back to English until translated).





SSO login → /cafe/dashboard | pass |
Switcher present; EN active; <html lang="en"> | pass |
| EN: 13 authed surfaces — no 5xx / pageerror / key-leak | pass |
Switch to KM → cookie set, <html lang="km">, KM active | pass |
| KM: 13 authed surfaces — no 5xx / pageerror / key-leak (English fallback) | pass |
| Login page honors KM cookie (public route) | pass |
| Staging smoke (demo Pro + lumiere Starter + guards) | 5/5 |
Pre-existing tz hydration warning on POS landing (formatTime lacks a timeZone → UTC vs local). NOT i18n-caused — now FIXED (APP_TIME_ZONE pinned). | fixed |
Polish pass when Khmer returns: dine-type 3 labels, 4 adapter throw-strings, flat-plural English ("1 orders") — consistent with the other 4 apps. | deferred |
nix-cafe staging 8a225fc
(Worker nix-cafe-staging 59ca459f). Parked for the scheduled prod promote — never ad-hoc (live get-coffee POS path).