Found while implementing #5406 (locale persistence). Filing only; deliberately out of that issue's scope — #5406's ruling was localStorage-only, with server-side preferences left for a separate decision.
What
apps/console/src/LocalizationFetchProvider.tsx already calls GET /api/v1/auth/me/localization (ADR-0053) and reads { currency, locale, timezone } off the response — but it feeds only LocalizationProvider, i.e. currency/number/date formatting. The locale value never reaches the i18n layer, so the console's UI language is decided entirely client-side (after #5406: stored choice → browser language → en).
Practical effect: a tenant whose configured locale is zh-CN still gets an English console on a new device / new browser profile / after clearing site data, until each user switches manually and re-establishes the local preference. The server already knows the answer at that moment and is asked for it on every boot.
Spec side, sys_user_preference exists as a declared system object — SystemObjects.USER_PREFERENCE, documented as "user preferences (theme, locale, etc.)" — so a per-user server-side home for this is contemplated by the platform, but @objectstack/client exposes no read/write API for it today.
Why it is not just "do it"
This is a contract question, not a bug fix, which is why it is a separate issue:
- Precedence. Does a server-side locale outrank a local explicit choice, or lose to it? (Reasonable reading: an explicit in-session choice wins for that browser; the server value seeds the first visit and syncs new devices.)
- Where it is stored. Reusing
sys_user_preference means a new client API surface (read at boot, write on switch) and a decision about whether the boot read blocks first paint — it would have to, or the flash-of-English it is meant to remove comes back. /auth/me/localization is currently a cosmetic, never-fail-closed, retried-in-the-background fetch precisely because nothing waits on it. - Write path. There is no endpoint today for a user to persist their own locale; that is a platform addition, not a console change.
Recording it so the maintainer can decide whether cross-device language sync is wanted, rather than having a dev infer it from the adjacent code.
Found while implementing #5406 (locale persistence). Filing only; deliberately out of that issue's scope — #5406's ruling was localStorage-only, with server-side preferences left for a separate decision.
What
apps/console/src/LocalizationFetchProvider.tsxalready callsGET /api/v1/auth/me/localization(ADR-0053) and reads{ currency, locale, timezone }off the response — but it feeds onlyLocalizationProvider, i.e. currency/number/date formatting. Thelocalevalue never reaches the i18n layer, so the console's UI language is decided entirely client-side (after #5406: stored choice → browser language →en).Practical effect: a tenant whose configured locale is
zh-CNstill gets an English console on a new device / new browser profile / after clearing site data, until each user switches manually and re-establishes the local preference. The server already knows the answer at that moment and is asked for it on every boot.Spec side,
sys_user_preferenceexists as a declared system object —SystemObjects.USER_PREFERENCE, documented as "user preferences (theme, locale, etc.)" — so a per-user server-side home for this is contemplated by the platform, but@objectstack/clientexposes no read/write API for it today.Why it is not just "do it"
This is a contract question, not a bug fix, which is why it is a separate issue:
sys_user_preferencemeans a new client API surface (read at boot, write on switch) and a decision about whether the boot read blocks first paint — it would have to, or the flash-of-English it is meant to remove comes back./auth/me/localizationis currently a cosmetic, never-fail-closed, retried-in-the-background fetch precisely because nothing waits on it.Recording it so the maintainer can decide whether cross-device language sync is wanted, rather than having a dev infer it from the adjacent code.