fix(spec): 视图标签按运行时真实下发的文档形状解析(#4854) - #5165
Merged
Merged
Conversation
…#4854) `resolveViewLabel` / `resolveViewDescription` read two fields the runtime's view document does not carry, so every view authored through `defineView` fell back to its English literal in every locale. Measured against the composer, not the bug report: `expandViewContainer` (`ui/view.zod.ts`) emits `{ name: '<object>.<key>', object, viewKind, label, config, order, scope }`, the ObjectQL engine registers that verbatim, and `RestServer` hands it to `translateMetadataDocument` unchanged. Two independent mismatches, either one sufficient: - the object is at top-level `object` (nested config data under `config`), not `objectName` / `data.object`, so the lookup bailed at the `!objectName` guard before consulting the bundle; - the lookup key was the namespaced registry identity `<object>.<key>`, while bundles key on the bare authoring key. `viewObjectName` now reads `objectName -> object -> data.object -> config.data.object`, matching the i18n extractor's own resolution order, and the lookup derives the bare key by stripping the `<object>.` prefix — a decode of the composer's rule, not a tolerant alias. `config.name` is deliberately not consulted: it is absent from every real view and, on a renamed colliding view, names a different view than the one being resolved. `ViewLike` gains two optional fields only; a bare `name` still resolves unchanged. Tests derive their fixture FROM `expandViewContainer` so they cannot drift from the serving path, and pin that the two old fields are absent from a real document. The four behavioural cases fail against the unfixed resolver. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ehu85kbvMcrNTUJjwxvLJ9
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 107 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
xuyushun441-sys
marked this pull request as ready for review
August 4, 2026 07:31
xuyushun441-sys
enabled auto-merge
August 4, 2026 07:31
This was referenced Aug 4, 2026
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 #4854
resolveViewLabel/resolveViewDescription读的两个字段,运行时下发的视图文档一个都没有,所以任何按defineView正常方式编写的视图,标签在任何语言下都只会落回英文字面量。列表视图切换器横在每个对象列表页顶部,在纯中文部署里这是屏幕上最显眼的残留英文。先测量,再动手 —— 下发形状确认属实(且有一处与 issue 描述不符)
没有采信 issue 里贴的 JSON,而是回溯到真正组装下发文档的代码,并用真实的应用元数据跑了一遍。
组装者:
expandViewContainerWithDiagnostics——packages/spec/src/ui/view.zod.ts:2203。每个展开项都由同一行产出,字段依次是name/object/viewKind/label/config(作者原始配置的深拷贝)/order/scope,其中name由${object}.${k}拼成,k是listViews/formViews的映射键。链路后续都不改形状:
packages/objectql/src/engine.ts:1875—— 引擎把展开项原样registerItem进注册表;packages/rest/src/rest-server.ts的GET /meta/:type—— 仅按viewKind存在且object等于查询参数来过滤、排序,再交给translateMetadataDocument翻译。对
examples/app-crm/src/views/lead.view.ts(真实编写的视图)实测下发结果:crm_lead.allcrm_leadcrm_leadcrm_lead.pipelinecrm_leadcrm_leadcrm_lead.web_to_leadcrm_leadcrm_leadcrm_lead.defaultcrm_lead结论:
object属实 ——objectName与顶层data确实都不存在,函数在if (!bundle || !objectName)处就返回了字面量,根本走不到查找;name被命名空间化为< object >.< 裸名 >属实;config.name并不存在。 上表四条真实视图没有一条带config.name,仓库内所有示例应用的视图配置也都没有写内层name。裸名的唯一可靠来源是顶层name去掉< object >.前缀之后的部分(即容器映射键);config.name只是作者可选字段。最后那条默认 form 更能说明问题:它连config.data.object都没有,顶层object是唯一能识别其对象的字段。因此实现按真实形状做,而不是按 issue 的措辞:查找键只由顶层
name剥前缀推导,不读config.name。理由写进了代码注释 —— 当发生重名改写(组装器会把冲突的< object >.< key >改写成..._2)时,config.name指向的是另一个视图,优先读它会把译文张冠李戴。裸键这一侧同样有两个独立佐证:i18n 提取器
pushViewEntries(packages/cli/src/utils/i18n-extract.ts:180)写出的就是裸键;已发布的平台包packages/platform-objects/src/apps/translations/*.objects.generated.ts里也全是sys_user._views.all_users这样的裸键,没有任何带前缀的。改动
viewObjectName依次取objectName→object→data.object→config.data.object。这个顺序刻意与 i18n 提取器自己的viewObjectName(i18n-extract.ts:163)保持一致 —— 写_views键的那一端和读它的这一端,从此对「哪个字段代表对象」给出同一个答案。viewTranslationKey:查找前剥掉${objectName}.前缀。这是对组装规则的反解,不是容错别名 —— 翻译包按裸键存放,而name携带的是注册表分配的全局唯一身份(ViewItemNameSchema)。没有前缀的名字(手工构造的视图)原样使用,行为不变。ViewLike只新增两个可选字段(object、config),纯类型增量,非破坏性。resolveViewDescription与resolveViewLabel共用这两个辅助函数,同一处缺陷一并修好 —— 只修标签会留下描述仍然失效。测试
packages/spec/src/system/i18n-resolver.test.ts新增一组用例,fixture 由expandViewContainer现场生成,而不是从 issue 里抄 JSON,所以它永远不会与真实下发路径漂移:组装器若改变身份拼写,断言随之改变,而不是继续描述一个运行时早已不产出的形状。覆盖:身份 pin(断言旧代码读的两个字段在真实文档里不存在,
config.name也不存在)、顶层object加裸键解析出译文、描述同样解析、locale 回退链、负向对照(对象存在但无对应_views条目 → 落回字面量)、无data的默认 form、经translateMetadataDocument的 REST 边界入口(并断言name/object/config原样保留)、前缀不属于本视图对象时不截断。反向验证(证明用例真的能抓到 bug):把
i18n-resolver.ts还原成修复前版本重跑,4 条行为用例全部失败,修复后全绿。门禁实测结果:
未新增导出、未改动任何 zod schema 的
.describe(),故 8 项生成物无需重新生成 —— 这是用check:generated实测确认的,不是推断。范围
按派发要求,未触碰 issue 中「report / dataset 没有 translator」一节 —— 那是独立的 surface 设计题,由 PM 另立单跟踪。本 PR 不新增翻译面,不改
METADATA_DOCUMENT_TRANSLATORS。顺带发现(已另立 issue,未在本 PR 修复)
#5164 ——
_views键的拼写有三个生产方互不一致:只声明默认list(没有listViews)的容器,提取器写出的键是list,而组装器给它的注册名是< object >.default,两边对不上,本 PR 之后这一种形状仍解析不出译文。这是生产方之间的分歧,必须在生产端统一;在消费端再加一层default转list的兼容,正是 Prime Directive #12 明令禁止的做法,所以留给该 issue 决策(倾向:以运行时身份的裸键default为准,并同步收紧 lint)。examples/app-crm之所以没暴露这个问题,只是因为它的默认list与listViews.all结构完全相同、被组装器的签名去重合并了,活下来的键正好是all。🤖 Generated with Claude Code
https://claude.ai/code/session_01Ehu85kbvMcrNTUJjwxvLJ9