Uh oh!
There was an error while loading. Please reload this page.
fix(i18n): 回填 console 命名空间 41 个缺失语言 key + console.ai.group. 前缀家族,十包补齐 (#3546 切片四) - #3839
Conversation
…#3546 切片四) `scripts/check-i18n-call-site-keys.mjs`(#3530)实测:`console` 命名空间有 41 个 key 被 `t()` 引用而**任何语言包都没有**,对应 **47 个调用点**(五个 key 多站点, `console.ai.dock.maximize` 有三个;分母一律脚本实测,不手数)。47 处全部写了内联 `t(key, { defaultValue: '英文' })`,即 #3517 那一类:英文照常渲染,十种语言统统 翻不了。裸 key 站点在切片一(PR #3583)已清完;对全仓 308 个 `console.*` 调用点 做 AST 扫描,本切片 key 的死兜底为 0 —— 所以不动任何组件文件。 - `en.ts` 补 46 key = 实测的 41 + 前缀家族 5。`console.notFound` 是新子命名空间, 其余扩充 `console.shortcuts` 与 `console.ai`(新增 `dock` / `designingPlanHint` / `group` 三个子对象)。41 个 key 的 en 值取调用点内联 defaultValue **逐字节相同**(脚本比对 46/47 站点)。 - 唯一不能相同的一处:`ChatDock.tsx:562/563` 用**同一个 key** (`console.ai.dock.open`)写了两句英文 —— `aria-label` 是 `Open assistant`, `title` 是 `Open assistant (⌘⇧I)`。一个 key 只能有一个值,en 取 aria-label 那句:无障碍名不能带被读屏念成符号的字形,且 `⌘` 是 mac 专有字形,语言包无法 按平台分叉。后果(title 不再提示该快捷键)记在 PR 正文并回填进 #3810。 - `console.ai.group.` 从棘轮 `missingPrefixes` 摘除(4 → 3)。它是模板 key (`ConversationsSidebar.tsx:277`),取值面是**封闭**的 `ConversationGroupKey` 联合,所以按枚举补齐五个成员而不是通配;测试读组件自己的联合与 fallback label 表,第六个分组一旦加入就红。 - 九包实译按同包 `console` 邻键取证(zh 全角标点与单破折号状态行、ja/ko 的 `AI ` 空格、de 敬语 Sie 与 `Wird …` 进行式、fr 直角撇号、es usted、pt `off-line` 拼法、ru ё、ar 动词居首不让 RTL 句首落 Latin token)。en 相同的字符串一律复用 邻键既有译文(`Go back` / `Publish failed` / `Try again` / `Back to home` / `Assistant` / `Today` / `Yesterday`)。 - 棘轮 `scripts/i18n-call-site-key-baseline.json` 精确减 42(41 key + 1 前缀): 109 → 68 key,4 → 3 前缀。切片二/三把总数硬钉在 109,两处同步改为 68。 - `zh` 的 `console.ai.planBuilding` 与 `AiChatPage.tsx:2195` 硬编码的 `convZh ? '正在搭建…'` 逐字节相同,并有断言读源码钉住两者 —— 那个三元本身是 独立缺陷(标签跟了会话语言而非 UI 语言),已立 #3837,不在本 PR 修。 Co-authored-by: Claude <noreply@anthropic.com>
The latest updates on your projects. Learn more about Vercel for GitHub. |
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
yinlianghui
commented
Aug 8, 2026
✅ 切片四验收:ACCEPT — PR #3839( 裁定要点:(1) 分母纪律四轮如一 —— 41+1 前缀实测、五个多站点 key 主动点名、棘轮 42 双向差集为空、ASSERT 全真;(2) 前缀家族的处理优于要求:按封闭联合 Generated by Claude Code |
Uh oh!
There was an error while loading. Please reload this page.
…3837) (objectstack-ai#3897) * fix(app-shell): 方案卡的 Building… 徽标随 UI 语言取值,不再被会话语言门控 (objectstack-ai#3837) `AiChatPage` 用 `convZh`(会话语言)门控四个字符串,因为 cloud 确认门 (`service-ai-studio` `confirm-gate.ts` `APPROVAL_RE`)只认中英,发进线程的 文本必须与线程本身同语言(objectstack-ai#772/objectstack-ai#2884)。该门上方注释的后半句写明了另一半规 则:button LABELS stay on the UI locale。 `planBuildingLabel`(objectstack-ai#2632 引入)落在了这句的错误一侧,中文会话恒取硬编码的 `正在搭建…`,两个后果都已实测: - **混合语言的卡片**:英文控制台里,中文会话的方案卡 `Proposed plan` / `Build it` / `Built` / `Not yet built` 全英文,中间夹一个中文徽标。objectstack-ai#2458 第 4 条记录的是同一个病的反方向。 - **翻译永远读不到**:中文会话恒走字面量,`console.ai.planBuilding` 的 zh 值对中文读者无效。objectstack-ai#3546 切片四(PR objectstack-ai#3839)刚把该 key 补进十包,只能做围 堵 —— 把 zh 值写成与字面量逐字节相同并把两者钉在一起。 徽标现在与相邻十二个标签一样读 `t('console.ai.planBuilding', …)`:十个包全 部可达(德文控制台 + 中文会话渲染 `Wird erstellt…`),zh 包成为该中文措辞的 唯一来源(值不变,中文读者看到的字符串与改动前完全一致)。 三条出站文本(`planApproveMessage` / `planApproveDefaultsMessage` / `changesConfirmMessage`)一行未动,仍随会话语言 —— 复核确认三者都交给 `onSendMessage`(`ChatbotEnhanced.tsx:1598`、`:2395`)、由门读取,正是 `convZh` 分支存在的那一类;全文件再无其它 `*Label`/`*Title` 被该门控住 (`convZh` 全部读点:定义 1 处 + 这三条)。 钉子: - `packages/app-shell/.../AiChatPage.planCardLocale.test.tsx`(新增)真渲染 `ChatPane` + 真 `I18nProvider` + 真 `isConversationZh`,把 `ChatbotEnhanced` 换成 props 记录器:en 控制台 + 中文会话 → `Building…` 且卡上 `*Label` 无一含中日韩字符;de 控制台 + 中文会话 → `Wird erstellt…` (三方语言证明是包在应答,而非两路三元);zh 控制台 → `正在搭建…`;三条 出站文本仍随会话。 - `packages/i18n/.../console-namespace-3546.test.tsx`:切片四埋的围堵钉按其 自述翻转 —— 从「字面量与 zh 包逐字节相同」改为钉住门已移除,并把不变量放 宽一步:`convZh` 只许门控出站 `*Message`,任何新读点都会红。 反向验证(方向先判后跑,与预判一致):还原三元 → 3 红 —— i18n 钉子按自定义 消息点名、en 控制台用例、de 控制台用例;zh 控制台用例与三条出站文本保持绿 (前者按设计逐字节相同,不具区分力,已在测试头注明)。 顺带:`conversationLanguage.ts` 模块头把 progress labels 也列进「随会话」, 该句在本改动后不再为真,收窄为「只治离开控制台发给 agent 的文本」。 写钉子时量到发送半边另有一个缺陷(zh 控制台 + 英文会话会把中文确认句发进英 文线程;`planAnswerMessage` 完全没有门控),不在本单范围,已立 objectstack-ai#3896。 测试:`packages/app-shell/src/console/ai` + `packages/i18n` 47 文件 654 通过; 两包 type-check 通过,全仓 `turbo run type-check` 78/78 通过;改动文件 eslint 零告警;`check-control-bytes` / `check-i18n-call-site-keys` / `check-i18n-en-drift` 均通过(零 en 值变更)。 Claude-Session: https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt Co-authored-by: Claude <noreply@anthropic.com> * test(app-shell): 钉子里的 agent 桩改用具名类型断言,不用 as never 评审可读性,零行为改动:`ChatPane` 的 agents 桩从 `as never` 改成 `as unknown as AgentDescriptor`(类型导入,运行期擦除;该模块在本文件里被 vi.mock,类型不受影响)。 Claude-Session: https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
Fixes-part-of #3546— 切片四。本单不关闭#3546:回填后仍余 68 个缺失 key + 3 个整族缺失前缀。基线
origin/main@c85268256。分母:脚本实测,不手数
node scripts/check-i18n-call-site-keys.mjs(#3530 的守卫)的analyze()实测本切片命名空间:console.*)missing-prefixconsole.ai.group.,棘轮四个前缀家族里唯一属 console 的)五个多站点 key(切片二"预测 90 实为 93"的教训,这次先量):
守卫读数,前后对照(同一命令):
解析数 +47(= 47 个调用点)、en key 数 +46,与清单精确相等。
棘轮 ASSERT 输出:
棘轮按行删除而非重序列化(先试了重序列化,
git diff变成 213 insert / 115 delete 的全文件重排;切片三的 diff 是干净的 54 行删除)。最终 diff:42 deletions(-),零插入。严重度:全部是"英文可见、十语不可译"
按切片一验收修正后的框架叙述。47 处全部带内联
defaultValue,无一渲染裸 key。zh会话下实际看到的:/ai连接横幅三句(Waiting for server…/Still working…/Connection lost — reconnecting…)全英文;propose_blueprint那段长等待的 lead-in 加十条轮播提示全英文;方案卡上Built/Not yet built/Published/Publish failed;ChatDock 整套 chrome(标题、拖宽把手、折叠、Open full page)全英文,其中两个是aria-label;会话侧栏日期分组读作Today/Yesterday/Previous 7 days;敲错 URL 得到英文Page not found;?快捷键面板里 AI 组是英文表格里的一块英文。其中Not yet built与TODAY正是 #2458 第 4 条点名的字串(已回填进度记在该单)。死兜底:先量再动,本切片 key 为 0(并顺带量出 5 处别人的)
对全仓 308 个
console.*调用点(不只本切片的 47 个)做 AST 扫描,找t(key) || '…'/?? '…'形态:5 处全在
ObjectView.tsx,且 key 都在en有值(所以||右侧恒不求值),不属本切片的 41 个 key,一处未动。切片一(PR #3583)删的是同一文件里 key 缺失的 5 处(那时||因 i18next miss 返回 key 恒真而失效);这 5 处从来没进过棘轮,谁也扫不到。已回填进 #3810(它正文自陈"规模未量"),并指出该单选项 B 的门禁若只判defaultValue会整支漏掉||。所以本 PR 零组件改动。en ↔ 内联 defaultValue:46/47 逐字节相同,1 处结构上不可能
ChatDock.tsx:562/563用同一个 key 在同一个按钮上写了两句不同的英文:key 缺失时两句今天都是活的(各渲染自己的 defaultValue);补进
en后包值恒胜,其中一句必然改变。这不是切片一cannotEditMetaView那种"四句从未渲染过的死英文"(那里合并不损失任何用户见过的东西),所以那条先例不覆盖它,必须裁一次。取
aria-label那句,两条理由:(1) 无障碍名不能带(⌘⇧I)这种被读屏念成符号的字形 —— #3546 切片一整条线就是无障碍名被 i18n miss 毁掉;(2)⌘是 mac 专有字形,en是语言包,无法按平台分叉(Windows/Linux 上真实绑定是 Ctrl+Shift+I,该串对非 mac 用户本来就是错的)。诚实记下后果:该
title的悬浮提示由Open assistant (⌘⇧I)变为Open assistant,不再提示快捷键。该快捷键是真的(chatDockState.ts:168的matchChatDockShortcut匹配 ⌘/Ctrl+Shift+I),但它没有出现在KeyboardShortcutsDialog的 aiChat 组(那里只有 ⌘⇧O 与 ⌘⇧S),所以补键之后它在 UI 上没有发现面了。是否把它加进快捷键面板属产品裁量,已连同这处分歧一并回填 #3810,不在本 PR 改组件。另注:ChatDockLauncher的注释写明 console 在 P3b 已退役该 launcher(只有 Studio dock 用它),所以这个提示的实际暴露面是 Studio 侧。前缀家族
console.ai.group.:按封闭联合枚举,不通配调用点是
t(`console.ai.group.${group.key}`, { defaultValue: CONVERSATION_GROUP_LABELS[group.key] })(ConversationsSidebar.tsx:277)。取值面不是开放字符串,而是同文件的封闭联合:所以修法是枚举五个成员,不是补一个通配前缀。测试读组件自己的联合与
CONVERSATION_GROUP_LABELS,钉两件事:en的console.ai.group子对象的键集合 === 联合成员集合(第六个分组一旦加入,这条红 —— 正是棘轮里那条 prefix 条目原本的职责,从棘轮搬进测试);en值与CONVERSATION_GROUP_LABELS的英文逐字节相同(包在时 i18next 答、包不在时 label 表答,用户不能分辨跑的是哪条)。这是"key 可达性 vs 值判定"两件不同的事,分别断言。棘轮
missingPrefixes4 → 3,切片三写的"恰 4 条"钉子同步改为 3 条并加一条not.toContain('console.ai.group.')。九包实译:逐包邻键取证
每包都在同一个包的
console邻域取证(不跨包套用):console.ai.offlineDemoMode=离线演示模式 — 无法获取助手列表(短状态行用单破折号加空格,长散文才用——);近 7 天的数字空格连接已断开 — 正在重新连接…;过去 7 天;全角,。console.ai.usage.title=AI 使用状況(AI后带空格);包内。88 处AI アシスタントを利用できません;プランをまとめています…publicForm.unavailableTitle=양식을 사용할 수 없습니다;包内无全角句号AI 어시스턴트를 사용할 수 없습니다;양식과 목록을 배치하는 중…console.ai.emptyDescription/planApproveHint用敬语 Sie;home.pendingDrafts.publishing=Wird veröffentlicht…Ihre App wird entworfen…、Wird erstellt…;KI-Assistent(承KI-Nutzung的连字符复合)calendar.today=Aujourd'hui(直角撇号,非 U+2019)Ouvrir l'assistant、7 jours précédentsconsole.ai.emptyDescription=…su aplicación actual、planApproveHint=Responda para…—— console.ai 邻域一致 ustedDiseñando su aplicación…、La URL que siguió…、reinténteloconsole.ai.offlineDemoMode=Modo de demonstração off-line(该包的 off-line 拼法)…temporariamente off-line — tente novamente…console.ai.emptyDescription用о чём、shortcuts.toggleDarkMode用тёмный—— 该包写 ёЕщё не собрано、Всё ещё выполняется…、не включён(有断言钉 ё)console.ai.*进行式用جارٍ …;包内把 AI 拼写为الذكاء الاصطناعي而非嵌 Latin 缩写جارٍ إعادة الاتصال…;三条 AI 相关句均动词/名词居首,并有断言"首字符非 Latin 字母"en 相同的字符串,一律复用邻键既有译文而不另造:
console.notFound.back= enGo back与detail.goBack同字符串,九包取同一译文;console.ai.publishFailed←home.pendingDrafts.publishFailed;unavailableRetry(Try again)←cloudConnection.retry(不是console.errors.tryAgain,后者 en 是Try Again,不同字符串);unavailableHome←marketplace.action.backHome;dock.title(Assistant)←console.ai.assistant;group.today←calendar.today;group.yesterday←dashboard.filters.range.yesterday。console.ai.published取各包console.ai.publishOk(Published — objects are now live.)的首句既有译法(已发布/公開しました/تم النشر…)。反抄袭断言:精确集合 1 对,单一理由
实测"与 en 逐字节相同"的
lang :: key对:1 / 414,只有一个:Assistant在法语里拼写完全相同,是真同形词,不是漏译 —— 与切片二的 12 对、切片三的 18 对同类。断言是集合相等而非子集:第 2 对同形会红,把这一对改成别的写法也会红、迫使出列。配 presence 断言防空转(切片二 B1 暴露的问题:集合相等在包为空时同样"绿"),并给出反面对照——同一个词嵌进句子里的两个法语邻键都与 en 不同形,证明豁免划在这一个词上,不是划在"法语包偷懒"上:
另加两条本切片特有的形状断言:零插值(这 46 个字串没有一个带 option,任何包加
{{x}}都会把花括号渲染给用户 —— 与切片三{seconds}那类正相反,所以钉的是"不许有")、省略号字符随 en(15 个以…结尾的 key,十包必须同样用 U+2026 而非三个半角点;实测十包在console.*里对 en 的…/...是逐 key 镜像的)。provider-mounted 抽样钉扎(discrimination check)
五个组件(
AiChatPage/ChatDock/ConversationsSidebar/AppContent的RouteNotFound/KeyboardShortcutsDialog)都是裸useObjectTranslation(),没有createSafeTranslationdefaults map 挡在前面 —— 所以挂 provider 不是仪式:不挂,回答问题的就不是 i18next,测的就不是 console 用的那条路。抽样(不是 46 key 全测):en/zh各解析 6 个代表 key(每组件/每子区一个),断言not.toBe(key)且等于包内值;t(`console.ai.group.${key}`))在 en/zh 两路把五个分组全渲染一遍;unavailableRetry、deplanDeferred、esnotFound.description、ptdock.collapse、ruconnectionStalled、jadesigningPlanHint.finalize、koshortcuts.groups.aiChat、arpublished;反向验证:三处全部预测为红/绿,三处全中
方向在跑之前就定了 —— 本切片是"包里没有 → 补上",没有被删的死枝、没有计数类下游门禁、没有 canonical-first 的
??链,所以不存在切片先例里的"诊断变多"或"反转"情形:第 1、2 条应为常规的红,第 3 条应为守卫绿而 parity/本测试红(那是 #3530 记录的门禁分工,不是漏检):en.ts(棘轮保持已删)→ 守卫报 48 条 unexpected / 42 distinct,退出码 1:zh.ts(en 保留)→ 守卫仍然绿(它只看 en),而 parity 与本切片测试红,失败文本就是前提本身:顺手量出、未在本 PR 修的事
AiChatPage.tsx:2195的planBuildingLabel被convZh门控住(convZh ? '正在搭建…' : t('console.ai.planBuilding', …))。同文件 1580-1586 行的注释自己写明规则是"发送内容随会话语言、标签随 UI 语言",而这是标签。后果:英文 UI + 中文会话时方案卡上其他标签全英文、中间夹一个中文;且中文会话恒走硬编码字面量,zh包这个 key 对真实中文用户无效。本 PR 的防护(不是修复):zh 值写成与该字面量逐字节相同的正在搭建…,并加断言读源码把两者钉住,三元被删时该断言会红提示回来清理:console.ai.dock.open的两句英文(该单类别的新实例,并说明它证明该单选项 A 对"一 key 多站点两句英文"无效、选项 B 的判据需加"同 key 各站点彼此也要相同");以及ObjectView.tsx5 处t(key) || '英文'死兜底(该单门禁设计若只判defaultValue会整支漏掉)。附本次console面的规模数据,回应该单正文自陈的"规模未量"。Not yet built/TODAY由本 PR 补齐,Your apps/Needs your attention仍在棘轮(home欠 5 个),Open in Builder →等属 cloud 侧产出、本仓守卫扫不到。未改该单状态(仍pm:on-hold)。验证
all-locales-key-parity.test.ts含在上面 30 个文件内,已绿(它是补完 en 后立刻要求九包补齐的那道门禁)。@object-ui/react只是type TranslationKeys的转出口(TranslationKeys = typeof en),全仓无任何消费方把它当必需形状声明,所以加 key 不会窄化谁 —— 已 grep 确认并实跑该包 type-check。测试命令按 AGENTS.md §9 的唯一正确写法(仓根
pnpm exec vitest run加相对仓根的路径),不用pnpm --filter 包名 test(陷阱一)、不把路径挂在--后(陷阱二)。围栏
packages/i18n/src/locales/*.ts(十包,仅新增本切片 key)、scripts/i18n-call-site-key-baseline.json(仅删本切片 42 条)、packages/i18n/src/__tests__/console-namespace-3546.test.tsx(新增)、auth-namespace-3546.test.tsx+organization-namespace-3546.test.tsx(仅棘轮总数 109 → 68,前者另同步missingPrefixes4 → 3 —— 那个计数器每切片动一次且只降)、changeset。零组件改动(本切片 key 的死兜底量出为 0)。与在飞 #3755/#3759(create-plugin)、#3812(components/action)、#3813(data-objectstack)、#3762(plugin-grid)零相交。未动content/docs/releases/**。