Skip to content

i18n coverage never gates view labels: every defineView() bundle is skipped by the !view.name guard #4123

Description

@os-zhuang

i18n/missing-view has zero producers. The 'view' source exists in the type, the walker has code for it, and the ratchet's header claims it's covered — but no view label in this repo has ever been gated.

证据

collectExpectedEntries() 跑在真实编译产物上(examples/*/dist/objectstack.json):

app-showcase: TOTAL expected entries: 2242 | source=view: 0

三个 example 全量:

exampleconfig.views[]named(会被 walk)匿名 bundle(被跳过)obj.listViewsbundle 内的视图 label
app-crm30309
app-showcase404020
app-todo10103

32 个用户可见的视图 label,一个都没被门禁看到。

根因

packages/cli/src/utils/i18n-extract.tscollectExpectedEntries 认两种形状,而编译产物两种都不是

  1. 对象内嵌(~行 367):for (const [viewName, raw] of Object.entries(obj.listViews)) —— 但编译后的 object 上没有 listViews(实测三个 example 全是 0)。

  2. 顶层具名视图(~行 392):

    constviews=Array.isArray(config?.views) ? config.views : [];for(constviewofviews){if(!view?.name)continue;// ← 这里

defineView() 编译出来的是一个匿名 bundle

// config.views[i]
{
"list": { "label": "All Tasks", "data": { "provider": "object", "object": "showcase_task" } },
"listViews": { "in_progress": { "label": "In Progress", "data": {} }, "urgent": {},  },
"formViews": { "edit": {}, "tabbed": {},  }
}

顶层没有 name,也没有 object / objectName —— 对象名藏在 list.data.object 里。所以 !view?.name每一个 bundle 都成立,循环第一行就 continue,全仓库的视图就此静默出局。

为什么这个洞会被漏掉

视图 label 是列表页顶部的视图切换 tab,用户天天看。example 声明了 i18n.supportedLocales: ['en','zh-CN',…],那些 tab 却全是英文,而 check:i18n-coverage 一直报绿。

i18n-coverage.ts 的文件头恰好写了这个失效模式:

this detector used to keep its own parallel walk, and the two drifted until whole declared surfaces were extractable but ungated

#3370 用"共用一个 walker"消除了 drift,但这次是 walker 自己与编译产物的形状脱节 —— 共用 walker 挡不住这一种。

同样地 os i18n extract 也不会为视图 label 播种骨架,作者手上没有可填的键。

影响面

  • CoverageIssue['source'] 里的 'view' 是死代码(doc 注释还给了示例 objects.account._views.all_accounts.label
  • pushViewEmptyState() 从未被有效调用过 —— 视图 emptyState 的 title/message 同样未被覆盖
  • scripts/check-i18n-coverage.mjs 的头部注释声称覆盖 listViews,与实现不符

修复方向

collectExpectedEntries 里认第三种形状:config.views[] 中不带 name 但带 list / listViews / formViews 的 bundle,逐个走进去,对象名从 list.data.object(以及各 listView 自己的 data.object)解析,键仍走既有的 objects.<obj>._views.<view>.* 约定。

注意:修完会让存量债务上涨(32 个 label × 每个非默认 locale)。按 5c8afed 立下的先例,应当翻译而不是抬 baseline——抬 baseline 等于把这个门禁存在的意义又放宽一次。

发现于 objectui#2960 的验证过程(framework#4101 加 showcase specimen 时顺带查证 i18n 门禁会不会变红,结果发现它根本不会)。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions