Repository navigation
页面组件文案没有翻译位:12 处用户可见字符串(首页占 10)在 zh/ja/es 下只能是英文,而 dashboards.widgets 有同形的翻译面 #1004
Description
Activity
- addedmetadataDeclarative metadata — schema, security posture, UI surfacesDeclarative metadata — schema, security posture, UI surfacesupstream:objectstackBlocked on / caused by the ObjectStack platform — tracked upstreamBlocked on / caused by the ObjectStack platform — tracked upstream
on Aug 6, 2026 上游镜像已建:objectstack-ai/objectstack#6080(Blocked-by)
按上游协议处理:本单症结在
@objectstack/spec的翻译契约(pages为.strict()四键、无组件文案面),不是 hotcrm 侧可修的——语言包写不进(strict 拒收)、运行时无处读,应用侧任何"修法"都是平台 workaround,⛔ 不做。- 镜像单含完整证据:契约形状、
title/subtitle归page:header的语义口径、与dashboards.widgets的不对称对照、12 处 dist 实测面,并给了建议方向(pages.components面与 widgets 对齐); - 本单标
pm:blocked+upstream:objectstack,待上游补面后回来消化(届时语言包补 zh/ja/es 三语、test/i18n-references.test.ts扩面覆盖组件键); - 同族 closing_this_quarter 的切换器页签标题写作 "Closing Soon" —— 与 label / 四个语言包的第四个拼法,且 tabs[].label 没有翻译位 #760(
tabs[].label无翻译位)维持原状,镜像单里已互相关联——上游若补面,两条一起解锁。
Generated by Claude Code
- 镜像单含完整证据:契约形状、
huangyiirene commented
on Aug 11, 2026 CollaboratorMore actionsRe-measured on
@objectstack/*17.0.0-rc.6 during the rc.5 → rc.6 upgrade (#1059, PR #1066). The asymmetry this card identified is closed upstream — option 1 in the disposition list ("上游补面") is what shipped.pagesin the translation contract is no longer four keys. It now carries acomponentsface, keyed by componentid, exactly parallel todashboards.widgets:pages: Record(name, { label?, description?, title?, subtitle?, components: Record(id, { title?, description?, label?, placeholder?, emptyText?, submitLabel? }) // strict })
The face is wider than this card asked for — beyond
title/description/labelit addsplaceholder,emptyTextandsubmitLabel— so it covers component copy this card did not enumerate. (settingsandmetadataFormsgained comparable faces in the same contract.)So all 12 strings listed here now have a translation slot: the 10 on
sales_home_page(quick_create.title, the four KPIlabels,ai_briefing.title/.description, etc.),lead_detail_page.related_tasks.title, andutility_bar_page.quick_notes.title. A zh-CN user's landing page no longer has to be two languages on one screen.Not done here: this PR is the dependency bump only. Actually populating
componentsacross the four locale bundles — and extendingtest/i18n-references.test.tsto walk the new face so the coverage guard can finally see page component copy — is app-side work with a real review surface. Worth reopening this card's scope as that follow-up, or filing it fresh.Scope note: static read of the shipped
@objectstack/spectranslation schema. Not verified in a browser that the console actually resolvespages.<name>.components.<id>.titleat render time — the point #1004's option 3 raised, and still the right thing to check before writing 12 strings × 4 locales.
Generated by Claude Code
Transfer sweep (#1156, part 1) — outcome 2: the fix lands in hotcrm. Relabelled and kept here.
The outcome test that was applied
Where would the fix land today? The
upstream:objectstacklabel was correct when applied on 2026-08-06 — the contract genuinely had no key to write into. It is stale now.Verdict: the upstream gap this card identified is closed; what is left is app-side
Platform half — shipped. The mirror objectstack#6080 ("翻译契约 pages 无组件文案面") is closed / completed, via merged PR objectstack#6466 ("feat(spec): translatable page component copy"). Verified upstream directly, not inferred from the on-card note.
The contract's
pagesface is no longer four strict keys. It now carries a component face keyed by componentid, parallel todashboards.widgets— which is precisely the asymmetry argument this card made, and option 1 of its own disposition list (「上游补面」) is what shipped. The delivered face is wider than this card asked for: beyondtitle/description/labelit also carriesplaceholder,emptyTextandsubmitLabel.So all 12 strings enumerated in the body now have a translation slot — the 10 on
sales_home_page,lead_detail_page.related_tasks.title, andutility_bar_page.quick_notes.title. The value chain this card described (literals in*.page.tsstraight through todist/objectstack.json, unwritable because.strict()rejects the key) no longer holds.App half — outstanding, and it is real work with a review surface. Populate the component copy across the four locale bundles, and extend
test/i18n-references.test.tsto walk the new face so the coverage guard can finally see page component copy — today it cannot, which is how this gap stayed invisible until #1002 tripped over it. That is app-side work in this repo, so there is no platform need left to route anywhere.Actions taken on this card
upstream:objectstack— removed. The upstream ask was made, accepted and shipped.pm:blocked— removed. The blocking condition (no key in the contract) is gone.prio:p2added. This card carried no priority grade at all. P2 (triaged backlog, no near-term schedule) is the honest grade: the user-visible symptom is real — a zh-CN user's landing page is two languages on one screen, on anisDefault: truepage shipped tosales_rep/sales_manager— but it is copy, not function, and nothing is broken.metadata— kept.- Left open. Not one of the 12 strings has been written.
⚠️ Carried forward: this card's option 3 is still unanswered, and it is the right first stepThe body's option 3 asked whether the console actually renders these literals — i.e. whether it resolves
pages.[ name ].components.[ id ].titleat render time, or only accepts the key in the schema. Everything established above is a static read of the shipped contract. Nobody has confirmed the render-time resolution in a browser.That matters practically: writing 12 strings × 3 locales against a key the console does not read would produce a silently ineffective change that still passes every guard. Check the render path first, then write the bundles.
⛔ And note what that check is not grounds for. If the console turns out not to resolve the key, that is a new finding against the console at that point — file it fresh, in
objectui, with the measurement. It is not a reason to keep this card labelledupstream:objectstacktoday on a defect nobody has observed.What was NOT done here
No app boot, no browser check, no code, no locale bundles touched. This sweep is routing and bookkeeping only. The upstream half was verified by reading objectstack#6080's state and its merged closing PR; the render-time question above is exactly the part that remains unmeasured, and it is named rather than assumed either way.
Related, unchanged
#760 (
tabs[].labelhas no translation slot) is the sibling case and was cross-linked from the upstream mirror on the expectation that both unlock together. This sweep did not touch it — it is not in part 1's six cards. Whoever picks up #1004 should re-check whether the shipped face covers #760 as well; if it does, #760 needs the same relabelling this card just got.Authority
- [Transfer sweep] Move all 22 platform-defect cards to the repo the fix lands in, and close them here with a pointer #1156 (transfer sweep), part 1.
- Maintainer ruling, 2026-08-14 — verbatim, untranslated:
hotcrm 席位的原则是用平台的能力做元数据应用的开发,平台的需求应该转给平台
This card is the ruling working as intended: the platform need was routed to the platform, the platform built the capability, and what returns is an app using it. The companion disposition 「关掉,只留指向镜像的指针」 does not apply — the mirror is already closed, and pointing a reader at a closed card would hide the app-side work that is the only thing left.
Generated by Claude Code
Generated by Claude Code
- added and removedupstream:objectstackBlocked on / caused by the ObjectStack platform — tracked upstreamBlocked on / caused by the ObjectStack platform — tracked upstream
on Aug 14, 2026 Triage (代扫, vacancy clause) →
pm:blocked,Blocked-by: objectstack-ai/objectstack#12231(filed by this seat just now).Filed 2026-08-06 with
metadata+prio:p2and no pm-state — nineteen days invisible. Vacancy clause: triage actions only.The card asked triage to choose among three routes. Two of them are already decided by the charter.
- Route 1, upstream — the charter's own instruction: 「平台相关的功能应该在平台中实现」. A missing face in the translation contract is as platform-side as it gets. Taken, filed as objectstack#12231 with the asymmetry, the 12-string surface and the boundary of what is measured.
- Route 2, app-side workaround — ⛔ not available in this lane. The charter forbids it in terms: 「永不在 hotcrm 内绕行 —— 绕行把平台缺口藏进样板,展品从此示范错误写法」. Moving copy onto whatever surface happens to have a slot, or writing an explicit exemption into a guard, is precisely hiding a platform gap inside the exemplar. Recording that here so the next reader does not re-open it as an option.
- Route 3, measure first — legitimate, and it is a question about impact, not existence, so it does not gate the filing. Carried into the upstream card as an explicit unestablished claim rather than dropped.
That leaves nothing this repo can do until the slot exists, which is the definition of
pm:blocked.⚠️ The part of this card that is inference, kept labelled as inferenceWhat is measured and renderer-independent: the key does not exist.
pagesis.strict()with four keys, so a locale pack is refused permission to carry component copy — this is not an English fallback, it is a closed door.What is inferred: that all 12 strings reach the screen as written. The sibling case #760 was filed on comparable static evidence about
list.tabs[].labeland was closed this week as premise-false — the switcher rendered a different string entirely and there was no user-visible symptom at all. Same family, same kind of static argument, opposite outcome on measurement.So the "同一屏两种语言" figure is the strong claim on this card and the least verified one. It is stated as such in objectstack#12231 rather than carried across as fact. ⛔ Whoever eventually implements this should not quote the 12-string count forward as a verified impact figure without a browser read.
prio:p2retained.
Generated by Claude Code
- addedpriority:p2Medium: important, M3Medium: important, M3and removed
on Sep 9, 2026
Blocked-by: objectstack-ai/objectstack#12231— ⛔ VOID, and it was never load-bearing. Removed from the reverse index at round 15. That upstream card was filed on 2026-08-25 asking for a face that had already shipped in@objectstack/spec17.0.0-rc.6 (measured on this card 2026-08-11, verified upstream 2026-08-14); it was closedcompletedon 2026-08-28. This repo is pinned at 17.1.0, so the slot exists in the package actually installed. See the round-15 claim comment.发现于 #1002 实施过程(改首页
ai_briefing卡片文案时,按裁定 grep 四个语言包找对应键,发现根本没有这个键面)。#1002 的文件面是那一张卡片,故单独立单。
现状(基线 origin/main = 86010ff)
翻译契约里
pages只有四个键,而且是.strict():(
node_modules/@objectstack/spec/dist/translation.zod-*.d.ts)pages现已带components面,见 2026-08-11 / 08-14 两条评论。保留原文是为了让这张卡的论证链可读,⛔ 不要把它当作今天的契约形状。test/i18n-references.test.ts的every page has translated nav copy and header copy也照着这四个键写,并且在注释里点明了title/subtitle的语义:它们对应page:header,因为 header 实例没有稳定 id,页面名是唯一能够到它的键。于是
page:header以外的组件文案 —— 卡片标题、卡片描述、指标块 label —— 没有任何翻译位。语言包写不进去(strict 会拒),运行时也无处可读,值链就是
*.page.ts里的字面量直通
dist/objectstack.json。实测面(从 dist 产物统计,非源码 grep)
8 个页面,12 处这样的用户可见字符串:
sales_home_page是isDefault: true、发给sales_rep/sales_manager的落地页。一个 zh-CN 用户登录后看到的首页,页头是「销售看板 / 欢迎回来,…」(有翻译位),而右侧
和左侧四张卡片、四个 KPI 块全是英文 —— 同一屏两种语言。
静态证据立单,实测后判 premise-false。⛔ 不要把 12 这个数字当作已验证的影响面向前引用。
这与 AGENTS.md 的 i18n 约束(「No new feature ships without all 4 locales」)以及
仓里 9 个 locale 相关守卫的一贯口径是冲突的:那些守卫覆盖对象/字段/视图/动作/选项,
唯独页面组件文案覆盖不到,因为契约里没有那个键可查。
为什么这像是契约的不对称,而不是本应用漏写
同一个翻译契约里,仪表盘的组件是有翻译位的:
widget 用
id定位就能翻译 title/description;page 的组件同样有稳定id(
ai_briefing、kpi_revenue_won…),却没有对应的components面。两个形状几乎一样的 UI 组件树,一个能翻一个不能,更像是 spec 侧少了一个面,而不是本应用的用法问题。
处置建议(不预判,交分诊)
三条路成本差别很大:
pages加components: Record( id, { title?, description?, label?, help? } ),与
dashboards.widgets对齐 —— 一致性最好,但要动@objectstack/spec;首页卡片只有英文并在守卫里把这一点写成明示豁免,免得下一个人再查一遍;
只是那一条是
tabs[].label)。本条的静态证据比 closing_this_quarter 的切换器页签标题写作 "Closing Soon" —— 与 label / 四个语言包的第四个拼法,且 tabs[].label 没有翻译位 #760 强(strict schema 直接排除了键的存在),但「渲染出来是什么」仍值得在浏览器里看一眼再定路线。
✅ 路线 1 已由上游交付(
⚠️ 路线 3 仍未做,且它是实施的第一步,不是可选项 —— 见 R15 派发令。
@objectstack/spec17.0.0-rc.6,面比本卡所求更宽:title/description/label/placeholder/emptyText/submitLabel)。⛔ 路线 2 本车道宪章明令不可用(绕行把平台缺口藏进样板)。
关联
tabs[].label也没有翻译位 —— 那条是视图切换器,本条是页面组件;两条合起来说明「没有翻译位」不是个别键的遗漏)
test/i18n-references.test.ts(语言包内部的键漂移 —— 与本条不同,那是有键但漂了,这里是根本没有键)