You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[Decision] One spelling for the signed-in user's language on the published surface — SessionUserSchema.language (unproduced), sys_user.locale (#13881) and /auth/me/localization.locale (request/deployment) coexist #14788
Filed by the domain:spec seat (session_017RbbUMnxkUnWhE4j94v8FE) from the contract review of PR #14775 (#13881, isolated reviewer verdict 5518923117, "Decisions to file for the maintainer" item 2). Decision inbox card — needs-user-decision; no domain:* / type set by this seat (triage's). PR #14775 lands without touching any of the three surfaces below; this card decides the follow-up.
Filed by the
domain:specseat (session_017RbbUMnxkUnWhE4j94v8FE) from the contract review of PR #14775 (#13881, isolated reviewer verdict 5518923117, "Decisions to file for the maintainer" item 2). Decision inbox card —needs-user-decision; nodomain:*/ type set by this seat (triage's). PR #14775 lands without touching any of the three surfaces below; this card decides the follow-up.背景
登录用户的「语言」在公开面上现在有三种拼法并存:
SessionUserSchema.language(packages/spec/src/api/auth.zod.ts:36,z.string().default('en'),描述 "Preferred language")—— 已发布的 session 契约,但全仓没有生产者也没有消费者(只有自己的 spec 测试);sys_user.locale—— [Decision] Per-user notification locale —— 2026-08-13 裁决所等的「实测拉力」已到(hotcrm:4 个已发布语言 × 16 个 notify 节点 × 0 可本地化) #13881 裁决新增的一等列(PR feat(spec,platform-objects,service-messaging,plugin-auth): sys_user.locale + per-recipient notification locale (#13881) #14775),通知投递按收件人读它;GET /auth/me/localization返回的locale(packages/plugins/plugin-auth/src/current-user-endpoints.ts~:876-888)—— 取的是请求/部署的 locale,不是用户列;objectui 曾用它播种 UI 语言(console: the tenant's server-sidelocale(/auth/me/localization) is fetched but never seeds the UI language — first login is always browser-or-en#5419)。#13881 的裁决明确排除「第二种拼法」,却没看见
language早已躺在契约上。复审判 PR 可以不动它先落地,本卡决定收口方向。带 re-check 命令的前提
language仍在契约上且无生产者:git grep -n "language" origin/main -- packages/spec/src/api/auth.zod.ts packages/plugins/plugin-auth/src packages/rest/src(只应命中 auth.zod.ts:36 与Accept-Language类用法)/auth/me/localization仍返回请求/部署 locale:git grep -n -B3 -A8 "localization" origin/main -- packages/plugins/plugin-auth/src/current-user-endpoints.tsSessionUser.language(未测,本席只有 objectstack 检出):在 objectui 仓git grep -n "\.language\b\|SessionUser" origin/main -- packages apps一句话问题
用户在个人资料里选了中文,前端问服务器「这个用户什么语言」时,契约上有一个永远是
'en'的language、一个不看用户只看请求的locale,和一个刚落地但契约上不可见的用户列 —— 三个答案,没有一个是对的。选项 × 真实代价
language继续躺在契约上,sys_user.locale只做存储language有意义,拿到永远的'en';用户选的语言 UI 永远看不到 —— 「声明 ≠ 兑现」原样留着locale到 session 契约,与language并存language → localelanguage(ADR-0049 enforce-or-remove),只在 session 端点真正产出时再以locale投影用户列/auth/me/localization改成「用户列 → 请求 → 部署默认」并成为唯一的读面language业务上:A = 产品文档里写着「支持用户语言」但设置无效;B = 设置页有两个「语言」下拉;C = 一次升级把老客户端弄坏;D = 像成熟 SaaS 那样,只有一个「我的语言」,后端按它答。
四轴(从业务立场)
language零拉动(从未产出、从未消费);用户列的 UI 侧拉动是真的(console: the tenant's server-sidelocale(/auth/me/localization) is fetched but never seeds the UI language — first login is always browser-or-en#5419 显示 console 曾试图从/auth/me/localization播种语言,而它今天不读用户列)。language就会用,拿到'en'还以为对了 —— 静默错误的温床;退役后读契约只剩一个locale,错了是响亮的编译期缺键。推荐 + 回退 + 置信缺口
SessionUserSchema.language(ADR-0087 登记),/auth/me/localization的locale改为「用户列 → 请求 locale → 部署默认」,成为唯一读面;不在 session 契约上另加字段,直到有真实生产者。SessionUser.language(未测 —— 执行卡第一步必须先测,命中即改判为 C 的兼容路径);/auth/me/localization的现有消费者对「用户列优先」的预期。裁后我会怎么执行(你不用管)
language→ 端点改读用户列 → 契约文本 + pin + changeset(spec minor,退役登记)。Refs
#13881(裁决 5494464459)· PR #14775(复审 5518923117,H3 finding)· #5419(console 播种 UI 语言,已关)· #14762 · ADR-0049 · ADR-0087
os-decision-facets
language,用户语言只经locale投影(D);B 扩生特例,A 留假声明,C 破坏性换同一结果。language零拉动(无生产者无消费者);用户列的 UI 播种拉动是真的(console: the tenant's server-sidelocale(/auth/me/localization) is fetched but never seeds the UI language — first login is always browser-or-en#5419)—— 拉动指向 D 的第二步,不指向保留language。language= AI 读契约拿到永远的'en'还以为对;删掉后错误是响亮的缺键。SessionUser.language与/auth/me/localization的引用未测。Generated by Claude Code