Files
Oxicloud/migrations/20260623000000_users_preferred_locale.sql
T
Edouard Vanbelle 7db27af7a6 feat(user.prefered_locale): save user's locale + invited have same locale as inviters
- OIDC JIT define the locale only at user creation, user can so change his preference later
    - invited users will inherit inviter's locale
    - email will use prefered_locale
    - login to a new browser will use prefered_locale
2026-06-03 14:37:22 +02:00

48 lines
2.9 KiB
SQL

-- ════════════════════════════════════════════════════════════════════════════
-- Per-user preferred locale (PR C of the i18n / magic-link templating work)
-- ════════════════════════════════════════════════════════════════════════════
-- Carries the user's preference for server-rendered surfaces — chiefly
-- transactional emails (invitation, login-via-email magic-link) and
-- future server-rendered HTML for authenticated users. The frontend
-- language switcher writes here via PATCH /api/auth/me/profile so the
-- choice survives across sessions and devices; the OIDC callback writes
-- here once at JIT provisioning if the IdP's `locale` claim resolves
-- against the LocaleRegistry; the magic-link invitation flow copies the
-- inviter's value into the new external user's row.
--
-- Value semantics:
-- NULL — no explicit preference. The application resolves to
-- OXICLOUD_DEFAULT_LOCALE (default "en"). NULL is also the
-- post-rollback shape; nothing reads this column in a way
-- that requires it to be set.
-- "xx" — IETF BCP-47 primary tag, e.g. "en", "fr", "ja".
-- "xx-YY" — primary + region subtag, e.g. "zh-TW".
--
-- The CHECK below enforces a permissive but bounded shape that matches
-- what the LocaleRegistry's case-insensitive comparison will canonicalise
-- successfully. We do NOT enforce membership in the registry's
-- discovered codes at the DB level — that list is build-time runtime
-- state, not schema. The application layer is the gatekeeper:
-- `update_profile_with_perms` rejects unknown codes with 400, and the
-- email-render path silently falls back to the server default when a
-- stored value no longer resolves (e.g. after dropping a locale file).
--
-- No backfill: every existing row stays NULL → inherits the server
-- default, which is the same behaviour every row had before this
-- migration. Pre-PR-C users see no change.
ALTER TABLE auth.users
ADD COLUMN preferred_locale TEXT NULL
CHECK (preferred_locale IS NULL
OR preferred_locale ~ '^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,8})*$');
COMMENT ON COLUMN auth.users.preferred_locale IS
'User-preferred locale for server-rendered surfaces (emails, future
auth pages). IETF BCP-47 shape: primary tag + optional subtags.
NULL = no preference; resolves to OXICLOUD_DEFAULT_LOCALE.
Set by: UI language switcher (PATCH /api/auth/me/profile),
OIDC JIT provisioning (one-shot, never re-applied on subsequent
logins — UI choice is canonical), inheritance from inviter at
external-user creation. Application enforces registry membership;
schema only constrains the textual shape.';