fix(i18n): resolve _views translation keys by the bare view name only (#3502) - #3768
Merged
Merged
Conversation
…ly (objectui#3502) `viewSuffixes` stripped the object prefix off the runtime view id to build the canonical `_views.<view>.<tail>` key, then — if that missed — tried the prefixed full name `_views.<object>.<view>.<tail>` as a second candidate. That made this client strictly more lenient than the server-side resolver, which reads the one bare key (objectstack#5165): a bundle authored against the prefixed spelling showed a translated label in the Console and English at the REST boundary, on mobile, over plain HTTP and in SDUI — every consumer that does not run a second resolution pass. A half-success is harder to spot than a clean miss. Third and last leg of the objectstack#5164 convergence (maintainer ruling A, 2026-08-06: canonical `_views` key = the runtime view identity's bare name). Leg 1 made the extractor ask the view composer for the key (objectstack#6124); leg 2 narrowed `packages/lint` to that single spelling (objectstack#6038); this resolver now accepts exactly what those produce. A prefixed key falls back to the metadata default on every surface alike, and is named at authoring time by `os lint`'s `translation-target-unknown` — loudness at the producer, per contract-first, rather than a lenient read here. The object-name axis is untouched: a bundle keyed by the short object name still resolves when the runtime presents the namespaced one. Tests pin both directions — bare key resolves (including when a prefixed sibling is present), prefixed-only falls back on label / description / emptyState — plus that the fallback adds no dev-console noise, since convention probes are deliberately excluded from the missing-key warner and would otherwise warn on every app that authored no view translations at all. No in-repo bundle used the prefixed spelling, so no fixture migration was needed. Co-Authored-By: Claude <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #3502
objectstack#5164 裁决 A(维护者 2026-08-06 批复)的第 3 棒 / objectui 段:
_views翻译键 canonical = 运行时视图身份的裸键,四面收敛。前两棒已落地 —— 提取器改为向组装器查询运行时身份键(objectstack#6124,b3c1f3cd5)、packages/lintcollectViewRecord收窄到单拼写(objectstack#6038,7618ee814)。本 PR 收掉最后一面:客户端二次解析。改了什么
packages/i18n/src/useObjectLabel.ts的viewSuffixes原本剥掉对象前缀得到裸键后,若裸键落空还会再试带前缀的全名作为第二候选:objects.crm_opportunity._views.pipeline_kanban.label(裸键,保留)objects.crm_opportunity._views.crm_opportunity.pipeline_kanban.label(带前缀全名,已删除)删掉第 2 候选后,按带前缀键编写的译文块完全不再被读取,标签回落到 metadata 默认文案 —— 与「根本没写译文」表现一致。按裸键编写的(提取器唯一产出、
os lint唯一接受的拼写)不受影响。对象名那一轴不动:
objects.{ns__obj}落空后仍退到objects.{obj},所以按短对象名写的译文包在运行时给出带命名空间对象名时照样命中。这是另一条正交的兜底,和视图名无关,已单独钉测试。为什么是删而不是留
服务端解析只认那一个裸键(objectstack#5165,刻意为之)。客户端多接受一个拼写 ⇒ 两端宽容度不对称:按带前缀键编写的包在 Console 里显示中文,而 REST 边界 / 移动端 / 纯 HTTP / SDUI 这些不做二次解析的消费方拿到的仍是英文。半成功比干净的落空更难被发现,还把一套第二方言事实上固化成了并行契约(违反 AGENTS #0.1 contract-first:宽容读取的正确修法在生产方)。
关于验收词「响亮回退」—— 需要如实说明它在本文件里落成什么:
I18N_PROBE_FLAG,被i18n.ts的 missing-key warner 跳过(见该文件 36-45 行的设计说明)。那个 hook 是在键落空时触发的,而「没写任何视图译文」恰恰是绝大多数 app 的健康状态 —— 让本路径去命中它,会在健康路径上刷警告,而不是在出错路径上。它也无法表达「键写了但拼写已被拒绝」这件事(那需要枚举 bundle)。os lint里被translation-target-unknown点名(第 2 棒 objectstack#6038 收窄后,该对象没有任何视图声明这个名字)。这符合裁 A 的「声明即强制」本义 —— 消费端不做第二份弱化副本。如维护者希望消费端另加一条一次性 dev 警告(仅当带前缀键确实存在于 bundle 时才响,零噪音),可以再开一单;本 PR 不擅自新增警告通道。
测试
packages/i18n/src/__tests__/useObjectLabel-view.test.tsx新增 4 例(原有 2 例保留、未改):label/description/emptyState三个走viewSuffixes的出口全部回落到默认文案(反向验证的锚点)。warnMissingKeys: true下console.warn零调用),把上面那条 probe 约定钉住,防止后人用「取消 probe 标记」来实现响亮。反向验证(先声明预期方向再跑):把删掉的第二候选加回去 → 预期 2 红 4 绿,红的是两条 prefixed-only 钉子(裸键在候选链首位,所以另外 4 例不该动)。实跑一致:
恢复改动后:
all-locales-key-parity在上面 26 个文件内一并跑绿。另外手工扫了改动文件的控制字节(grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f]',零匹配)。存量键排查(派发说明第 3 条)
全仓
git grep _views(排除 CHANGELOG):没有任何仓内译文包/fixture 使用带前缀拼写 ——packages/i18n两个测试与spec-translations都是裸键,app-shell/layout的命中全是list_views元数据或注释。第 1 棒已把 showcase / app-todo / app-crm 的存量迁完(在 objectstack 仓),因此本 PR 无 fixture 改名,消费方测试(viewLabel全为 mock)也无一依赖被删的那条候选。影响面
用户可见行为变更,已带
.changeset(@object-ui/i18npatch,含 FROM→TO 迁移指引:去掉键里的对象前缀)。按仓库约定不标major。Generated by Claude Code