fix(metadata-admin): block-config 的 placeholder 提示随语言切换(#3979) - #3993
Merged
Conversation
#3913 把 block-config 的 label/addLabel/option label 搬成翻译键就停在了 相邻的一列。18 个 placeholder 里有 8 个是语言内容,至今仍是显示文本: zh-CN 管理员打开 page:header,「图标」下面的输入框提示还是 `lucide icon name`;record:details 的「名称(i18n 键)」下面是 `snake_case, e.g. contact_info` —— 正好在 #3913 / #3963 刚修好的面板里。 这 8 个改为 `engine.inspector.pageBlock.placeholder.<blockType>.<字段路径>`, 渲染处过 t();en 值与被替换的字面量逐字节相等(含 U+2026 省略号)。 placeholder 不能照抄 label 那条:它是**混合面**。另外 10 个是要精确输入的 **值** —— 行数 `20`、`https://…`、内联动作样例 `{ "type": "url", "target": "/environments" }` —— 翻译它们是缺陷而不是善意: 被"翻译"过的 JSON 样例是 InlineActionSchema 会拒的元数据。 所以两种含义写进类型,而不是靠约定加豁免清单: placeholder?: { key: string } | { literal: string } 裸字符串不再可赋值,`placeholder: 'lucide icon name'` 现在是类型错误 —— type-check 在每条流水线和编辑器里都跑,错误在写下的地方就被拦住,而不是 变成中文面板里的英文框等人看见。placeholder 保持可选(无提示的字段是合法的), 只有 addLabel 那条按 #3913 改了必填。 钉子:走键的一半并入 #3913 的位置推导断言,成为第五个键族(families.placeholder = 8,新增的 prose placeholder 缺翻译即红);保持字面的一半钉成逐条带理由的 台账 + "字面不得是散文"的哨兵,因为"顺手把另外十个也翻掉"是最可能的下一次编辑; 渲染面在 PageBlockInspector.i18n.test.tsx 加了成对断言(prose 随语言变、值不变), 并补了 placeholder 属性专用的裸键扫描 —— textContent 看不见属性,这正是 #3963 那两个 placeholder 躲过 #3913 评审的缝。 block-config.test.ts 的 snake_case 断言从原始 placeholder 移到**解析后**的提示 且两语都断言,这是更强的说法:snake_case 这个 token 必须活过翻译。 Fixes #3979 Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
Collaborator
Author
|
PM 验收(session_01GTRjn8xBqp75dk7kFupVRt):通过,转 ready 并挂 auto-merge。#3979 落地 —— PageBlockInspector i18n 三部曲(#3913 label / #3963 chrome / #3979 placeholder)收官。 核验记录(head
concern 中 actionNames 译文与 label 近重复为忠实翻译 —— 同意,措辞调整属维护者口味留原样。out-of-scope #3994(检查器共享字段组件无可访问名,16 文件消费面)由 PM 分诊(另评)。 Generated by Claude Code |
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 #3979
#3913(PR #3970)把
block-config.ts的label/addLabel/ option label 搬成翻译键,停在了相邻的一列。18 个placeholder里有 8 个是语言内容,至今仍是显示文本:zh-CN 管理员打开page:header,「图标」下面的输入框提示还是lucide icon name;record:details的「名称(i18n 键)」下面是snake_case, e.g. contact_info—— 正好在 #3913 / #3963 刚修好的那个面板里。本 PR 把这 8 个改为
engine.inspector.pageBlock.placeholder.后接 blockType 与字段路径的键,渲染处过t(),en-US / zh-CN 双语补齐。en 值与被替换的字面量逐字节相等(含Text…的 U+2026 省略号,已用脚本按 hex 比对),所以英文面板没有任何变化。逐条判定表(18 条,判定理由在最后一列)
object-grid.pageSize20object-form.columns2object-metric.iconlucide icon namelucide作为库名在 zh 里保留grid.columns3grid.gap4element:text.contentText…element:image.srchttps://…element:definition-list.columns1element:repeater.limit10element:button.iconlucide icon nameelement:button.action{ "type": "url", "target": "/environments" }InlineActionSchema会拒的元数据page:header.iconlucide icon namerecord:related_list.limit10record:details.sections.namesnake_case, e.g. contact_infosnake_case与contact_info两个 token 在 zh 值里原样保留(snake_case,例如:contact_info)。仓内已有同形先例:engine.inspector.view.objectPlaceholder=e.g. crm_lead→ 「例如:crm_lead」,engine.inspector.report.namePlaceholder=e.g. pipeline_by_stage→ 「例如:pipeline_by_stage」record:details.sections.columns2record:alert.iconlucide icon namerecord:quick_actions.actionNamesaction nameai:input.agentNameagent name合计 8 键 / 10 字面,与 issue 正文的 8/10 拆分一致。
派发提示写的是「lucide 图标名 ×4 ... 保持字面」,但 issue 正文的命中表把这 4 条列在「8 个有语言内容」之内,而且 issue 标题与「影响」段正是拿它举证(「打开
page:header,字段名「图标」是中文,它下面输入框的提示是lucide icon name」)。逐条判定的结论站正文一边,理由是:lucide icon name不是要输入的值 —— 值是users/bar-chart这类图标名。它是描述"该输入什么"的英文名词短语,与20/https://…/ JSON 样例不同类。engine.inspector.pageBlock.objectPlaceholder(snake_case object→ 「snake_case 对象名」)与fieldPlaceholder(field name→ 「字段名」)在 PageBlockInspector 自身的 chrome 字面量未过 t() —— 列表「Add」按钮、Remove aria-label、Invalid JSON、两个 placeholder 在 zh-CN 下仍是英文 #3963 里已经是翻译键。让lucide icon name保持字面,会让同一面板的同类提示一半中文一半英文。lucide保留,只翻译其余散文 —— 与snake_case 对象名完全同形。若维护者仍要求 4 条保持字面,回退面很小:4 行
{ key: … }改回{ literal: … }、删 8 行译文、把families.placeholder由 8 改 4、并把这 4 条加进字面台账。机制:混合面写进类型,而不是"约定 + 豁免清单"
issue 正文把这条列为留给实施者论证的契约面小决定(两个选项:显式豁免台账,或在
BlockPropField上拆成两个字段)。本 PR 取第三种、也是两者的严格上界 —— 一个可辨识联合:placeholder保持可选(无提示的字段是合法的);只有addLabel在 页面块检查器的 PROPERTIES 面在 zh-CN 会话下是英文的 —— curated 字段 label 走字面量、只有外围章节标题走t()#3913 里改了必填。placeholder: 'lucide icon name'(本文件在此前的写法,也是任何生成新字段的东西最先伸手去写的写法)现在是类型错误。这比那 8 个字符串本身更要紧 ——type-check在每条流水线和编辑器里都跑,错误在写下的地方就被拦住,而不是变成中文面板里的一个英文框、等某个评审者注意到。never让"两个都填"也编译不过,渲染处不需要在两者之间挑赢家。placeholder=站点(number / json / string-list / 默认 text)收敛到一个placeholderText()解析器 —— 面板内容当初之所以在中文面板里是英文,就是因为有一列绕过了访问器。键形沿 #3913 的位置推导惯例(
engine.inspector.pageBlock.placeholder.+ blockType + 字段路径),因此并入了那条结构钉的推导断言,成为第五个键族;推导测试不需要新族的例外分支,只多了一次walk里的采集。4 个 lucide 提示是 4 个不同的键、英文文本相同 —— 与 field 键携带 blockType 同理:一个键不能说两件事,将来某个块想换措辞不必先拆键。钉子
block-config-i18n.test.ts:families.placeholder精确等于 8(像add那样精确,新增的散文 placeholder 缺翻译即红);en 侧 8 个键全部进en-US labels are unchanged样本(不是抽样 —— 这一列是手工迁移的,en 是"键能解析"与"键解析出原来那句话"之间唯一的东西);zh≠en、两语齐备、位置推导相等由既有的 5 条断言自动覆盖。because必须 > 10 字符)+ 一条"字面不得是散文"的正则哨兵(相邻两个字母词 = 句子的签名;{ literal: 'lucide icon name' }会被它抓住,单词提示不会 —— 所以台账是穷举而非启发式)。理由:"顺手把另外十个也翻掉"是最可能的下一次编辑。PageBlockInspector.i18n.test.tsx加了成对断言:散文随语言变、值不变(同一个record:detailsfixture 里sections.name变、旁边的sections.columns=2不变)。另补expectNoRawKeyPlaceholders()—— placeholder 是属性,textContent看不见它,既有的expectNoRawKeys()结构上抓不到裸键进 placeholder,这正是 PageBlockInspector 自身的 chrome 字面量未过 t() —— 列表「Add」按钮、Remove aria-label、Invalid JSON、两个 placeholder 在 zh-CN 下仍是英文 #3963 那两个 placeholder 躲过 页面块检查器的 PROPERTIES 面在 zh-CN 会话下是英文的 —— curated 字段 label 走字面量、只有外围章节标题走t()#3913 评审的缝。block-config.test.ts的expect(nameField.placeholder).toMatch(/snake_case/)属"钉住了被改掉的形态"——改判为断言解析后的提示且两语都断言(与同一测试里 页面块检查器的 PROPERTIES 面在 zh-CN 会话下是英文的 —— curated 字段 label 走字面量、只有外围章节标题走t()#3913 已经这么做的 label 断言同形)。这是更强的说法:snake_case这个 token 必须活过翻译,而不只是在英文里存在。json placeholder 那条从"存在"加强到"必须是 literal"。PageBlockInspector.sectionName.test.tsx的getAllByPlaceholderText(/snake_case/i)无需改动 —— 它跑 en-US,而 en 值逐字节不变(反向验证里它是最响的一处红)。反向验证(先预判方向,再跑;变异未提交)
M1 —— 渲染处不再调
t()(placeholderText直接返回p.key):passes example VALUES through untranslated应保持绿(字面值有没有t()都一样),另外PageBlockInspector.sectionName.test.tsx(metadata-admin 的record:details.sections块设计器没有name输入位 —— Studio 搭出来的 section 结构上无法翻译,且必然挂着上游translation-section-name-missing#3819)应红 —— 它按/snake_case/i定位输入框,裸键里没有这个 token。Tests 11 failed | 50 passed—— block-config.ts 的 8 个 placeholder 仍是英文显示文本 —— #3913 只搬了 label/addLabel/option,zh-CN 面板里的输入框提示还是 "lucide icon name" #3979 的 6 条里 5 条红(第 6 条passes example VALUES…如预判保持绿)、sectionName(metadata-admin 的record:details.sections块设计器没有name输入位 —— Studio 搭出来的 section 结构上无法翻译,且必然挂着上游translation-section-name-missing#3819)6 条红、block-config-i18n.test.ts(15)与block-config.test.ts(16)全绿。方向与预判一致,包含那条"表钉看不见渲染"的分工:这正是这两个文件各自存在的理由,页面块检查器的 PROPERTIES 面在 zh-CN 会话下是英文的 —— curated 字段 label 走字面量、只有外围章节标题走t()#3913 的文件头也这么写着。M2 —— 把一个字面值改成键(
element:button.action的 JSON 样例改{ key: … },不加译文):block-config.test.ts的 json-must-be-literal 红;block-config-i18n.test.ts的families.placeholder(8→9)、字面台账等值、en/zh 解析三条红;渲染面passes example VALUES through untranslated红。Tests 8 failed | 45 passed,红的正是上述 8 条(3 个文件都红)。方向与预判一致。两次变异后
git checkout --复原,git status干净,树等于提交622ff385。验证
6bd6a4d76;git merge-base --is-ancestor 708aaf8e4 HEAD通过(PageBlockInspector 自身的 chrome 字面量未过 t() —— 列表「Add」按钮、Remove aria-label、Invalid JSON、两个 placeholder 在 zh-CN 下仍是英文 #3963 的 PR fix(metadata-admin): 页面块检查器自身的 chrome 跟随会话语言 (#3963) #3980 在基内)。pnpm --workspace-concurrency=2 --filter '@object-ui/app-shell^...' build—— 先行,通过。pnpm exec vitest run packages/app-shell/src/views/metadata-admin --maxWorkers=2——Test Files 135 passed (135),Tests 1372 passed | 1 skipped (1373)。pnpm exec turbo run type-check --concurrency=2——78 successful, 78 total。check:i18n-keys——Every in-scope call-site key resolves against the en pack (2918 keys);check:i18n-drift——0 en value(s) changed(本 PR 只新增键,不改既有 en 值;metadata-admin 的i18n.ts是模块内表、按设计不在 call-site 门的扫描面内)。node scripts/check-control-bytes.mjs—— OK(3889 个文件);另按控制字节纪律自查grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f]'全部改动文件,无命中。.changeset/block-config-placeholder-i18n-3979.md,@object-ui/app-shellpatch;check-changeset-fixed/check-changeset-no-major通过。边界
BlockPropField没有格式/校验能力位 —— 所有标识符类字段(sectionname、tabkey、accordionvalue)只能靠 placeholder 陈述约定 #3912 的 capability 面(BlockPropField仍没有 pattern/validate 能力位;sections.name依旧只能靠 placeholder 陈述 snake_case 约定 —— 本 PR 只保证这句约定会翻译,不去加能力位)。select分支把 host id 展开到 Radix Select.Root —— 手写 form schema 里type: 'select'的 labelfor全部悬空(#3306 的内建侧同形) #3976 / [plugin-grid] BulkActionDialog 的必填 param 把裸*折进可访问名、且required从不下发到控件 —— #3299/#3290 家族里被漏掉的一个站点 #3967 的文件;未碰content/docs/releases/。previews/block-config.ts、metadata-admin/i18n.ts、inspectors/PageBlockInspector.tsx、inspectors/PageBlockInspector.i18n.test.tsx、previews/__tests__/block-config-i18n.test.ts、previews/__tests__/block-config.test.ts。Generated by Claude Code