Skip to content

fix(app-shell): 方案卡的 Building… 徽标随 UI 语言取值,不再被会话语言门控 (#3837) - #3897

Merged
yinlianghui merged 2 commits into
mainfrom
claude/issue-3837-planbuilding-ui-locale
Aug 9, 2026
Merged

fix(app-shell): 方案卡的 Building… 徽标随 UI 语言取值,不再被会话语言门控 (#3837)#3897
yinlianghui merged 2 commits into
mainfrom
claude/issue-3837-planbuilding-ui-locale

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Fixes #3837

AiChatPageconvZh(会话语言)门控四个字符串,因为 cloud 确认门(service-ai-studio confirm-gate.ts APPROVAL_RE)只认中英,发进线程的文本必须与线程本身同语言(#772 / #2884)。该门上方注释(AiChatPage.tsx:1581-1586)的后半句写明了规则的另一半:

> button LABELS stay on the UI locale.

planBuildingLabel(#2632 引入)落在了这句的错误一侧,中文会话恒取硬编码的 正在搭建…

两个后果(都已实测)

改动前 改动后
en 控制台 + 中文会话 Proposed plan / Build it / Built / Not yet built 之间夹一个 正在搭建… Building…
de 控制台 + 中文会话 正在搭建… Wird erstellt…
zh 控制台 正在搭建…(来自字面量) 正在搭建…(来自 zh 包)

第二个后果是翻译永远读不到:中文会话恒走字面量,console.ai.planBuilding 的 zh 值对中文读者无效 —— 改措辞不产生任何效果。#3546 切片四(PR #3839)刚把该 key 补进十包,只能做围堵:把 zh 值写成与字面量逐字节相同,并在测试里把两者钉在一起。#2458 第 4 条记录的是同一个病的反方向(中文界面里漏出英文)。

改法

一行:徽标与相邻十二个标签一样读 t('console.ai.planBuilding', …)。十个包全部可达,zh 包成为该中文措辞的唯一来源(值不变,中文读者看到的字符串与改动前完全一致)。

三条出站文本(planApproveMessage / planApproveDefaultsMessage / changesConfirmMessage)一行未动。核验结果:三者确为发送内容 —— 都交给 onSendMessage(ChatbotEnhanced.tsx:1598:2395),由确认门读取,正是 convZh 分支存在的那一类。全文件再无其它 *Label / *Title 被该门控住:convZh 的读点就是「定义 1 处 + 这三条」。

钉子

  • 新增 packages/app-shell/src/console/ai/__tests__/AiChatPage.planCardLocale.test.tsx —— 真渲染 ChatPane + 真 I18nProvider + 真 isConversationZh 探针,把 ChatbotEnhanced 换成 props 记录器(ChatPane 交下去的 props 就是这两条规则所在之处)。四例:en 控制台 + 中文会话 → Building… 且卡上 *Label 无一含中日韩字符;de 控制台 + 中文会话 → Wird erstellt…(第三方语言才能证明是包在应答,而不是一个两路三元);zh 控制台(两种会话)→ 正在搭建…;三条出站文本仍随会话语言。
  • 翻转 packages/i18n/src/__tests__/console-namespace-3546.test.tsx 里切片四埋的围堵钉 —— 其自述就是「在这个三元被删时红,提示回来清理」。现在钉的是门已移除,并把不变量放宽一步:convZh 只许门控出站 *Message,任何新读点(尤其喂给渲染的)都会红并点名。

反向验证(方向先判后跑)

预判:还原三元 → en / de 两例红(徽标变回 正在搭建…)、i18n 钉子红并点名;zh 控制台一例与三条出站文本保持绿 —— 前者按设计与字面量逐字节相同,不具区分力(与切片四「en 断言无法区分前后」同一个道理,已写在测试头)。

实跑与预判一致:3 failed | 35 passed,红的三条正是上述三条,i18n 钉子的报错是自定义消息 the convZh gate is back over planBuildingLabel — the zh pack value is dead again (#3837)

顺带

conversationLanguage.ts 模块头把 progress labels 也列进「必须随会话」,该句在本改动后不再为真(它现在只治离开控制台发给 agent 的文本)—— 收窄了这一句,并写明标签走 UI 语言。这是本 PR 所收窄的同一条规则的陈述,不是无关顺手活。

越界发现(未在本 PR 修)

写钉子时量到发送半边另有缺陷,已立 #3896(未打标签,待 PM 分诊):三条出站文本的 t() 分支读的是 UI 包,而 zh 是唯一被允许(且被要求)定义这些 key 的非 en 包,所以 zh 控制台 + 英文会话会把 确认,开始搭建。 发进英文线程(#2884 症状的反向触发);planAnswerMessage 属于 OUTBOUND_KEYS 却完全不过 convZh。因此本 PR 的出站断言故意用 de 控制台(该包不定义这些 key,保证落到英文默认值),而不是把 zh 控制台的现状钉成正确。

测试

  • pnpm exec vitest run packages/app-shell/src/console/ai packages/i18n --maxWorkers=247 文件 / 654 通过
  • pnpm --filter @object-ui/app-shell --filter @object-ui/i18n type-check → 通过;全仓 turbo run type-check --concurrency=278/78
  • 改动文件 eslint 零输出;check-control-bytes / check-i18n-call-site-keys / check-i18n-en-drift(零 en 值变更)均通过
  • changeset:@object-ui/app-shell patch

Generated by Claude Code

`AiChatPage` 用 `convZh`(会话语言)门控四个字符串,因为 cloud 确认门
(`service-ai-studio` `confirm-gate.ts` `APPROVAL_RE`)只认中英,发进线程的
文本必须与线程本身同语言(#772/#2884)。该门上方注释的后半句写明了另一半规
则:button LABELS stay on the UI locale。

`planBuildingLabel`(#2632 引入)落在了这句的错误一侧,中文会话恒取硬编码的
`正在搭建…`,两个后果都已实测:

- **混合语言的卡片**:英文控制台里,中文会话的方案卡 `Proposed plan` /
  `Build it` / `Built` / `Not yet built` 全英文,中间夹一个中文徽标。#2458
  第 4 条记录的是同一个病的反方向。
- **翻译永远读不到**:中文会话恒走字面量,`console.ai.planBuilding` 的 zh
  值对中文读者无效。#3546 切片四(PR #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` 完全没有门控),不在本单范围,已立 #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>
@vercel

vercel Bot commented Aug 9, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectui Ignored Ignored Aug 9, 2026 12:43am

Request Review

@github-actions github-actions Bot added the tests label Aug 9, 2026
评审可读性,零行为改动:`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>
@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Main entry (gzip) 28.1 KB 350 KB
Entry file index-DkbNpKCb.js
Status PASS

📦 Bundle Size Report

Package Size Gzipped
app-shell (index.js) 8.66KB 3.13KB
app-shell (runtime-config.js) 7.42KB 2.32KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 7.57KB 2.97KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 1.17KB 0.53KB
auth (AuthProvider.js) 22.10KB 4.37KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.13KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.64KB 2.21KB
auth (SocialSignInButtons.js) 9.60KB 3.89KB
auth (UserMenu.js) 3.40KB 1.22KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 35.76KB 9.11KB
auth (createAuthenticatedFetch.js) 4.37KB 1.69KB
auth (index.js) 2.35KB 1.07KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 4.91KB 0.87KB
auth (useIsWorkspaceAdmin.js) 1.61KB 0.85KB
collaboration (CommentThread.js) 26.07KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.65KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 482.39KB 106.34KB
core (index.js) 2.96KB 1.13KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 139.61KB 35.99KB
fields (index.js) 230.82KB 56.70KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (i18n.js) 4.32KB 1.77KB
i18n (index.js) 2.65KB 1.06KB
i18n (pickLocalized.js) 1.70KB 0.83KB
i18n (provider.js) 9.48KB 3.27KB
i18n (useObjectLabel.js) 27.59KB 6.63KB
i18n (useSafeTranslation.js) 4.52KB 1.96KB
layout (index.js) 38.53KB 10.71KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.74KB
mobile (index.js) 1.50KB 0.62KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.71KB 0.42KB
mobile (useResponsiveConfig.js) 1.36KB 0.63KB
mobile (useSpecGesture.js) 4.32KB 1.64KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 8.75KB 3.06KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 3.67KB 1.12KB
permissions (evaluator.js) 4.41KB 1.44KB
permissions (index.js) 0.91KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.52KB
permissions (usePermissions.js) 1.55KB 0.71KB
plugin-ai (index.js) 15.71KB 3.79KB
plugin-calendar (index.js) 44.98KB 12.37KB
plugin-charts (index.js) 61.04KB 17.31KB
plugin-chatbot (index.js) 180.33KB 42.79KB
plugin-dashboard (index.js) 117.21KB 30.27KB
plugin-designer (index.js) 210.51KB 42.51KB
plugin-detail (index.js) 236.49KB 58.96KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 112.10KB 27.10KB
plugin-gantt (index.js) 162.55KB 39.57KB
plugin-grid (index.js) 187.63KB 49.66KB
plugin-kanban (index.js) 48.30KB 13.28KB
plugin-list (index.js) 105.12KB 25.48KB
plugin-map (index.js) 16.81KB 5.24KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 40.58KB 10.58KB
plugin-timeline (index.js) 25.76KB 7.33KB
plugin-tree (index.js) 8.50KB 2.88KB
plugin-view (index.js) 84.03KB 20.55KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.71KB 3.53KB
providers (index.js) 0.44KB 0.22KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.67KB 2.37KB
react (LazyPluginLoader.js) 3.77KB 1.33KB
react (SchemaRenderer.js) 19.28KB 6.38KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 1.02KB 0.55KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 4.09KB 1.74KB
sdui-parser (index.js) 4.47KB 2.03KB
sdui-parser (parse.js) 10.04KB 2.82KB
sdui-parser (types.js) 0.29KB 0.24KB
sdui-parser (validate.js) 4.69KB 1.48KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 0.99KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 0.20KB 0.18KB
types (crud.js) 0.20KB 0.18KB
types (data-display.js) 0.20KB 0.18KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.87KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-retry.js) 4.32KB 2.02KB
types (index.js) 2.71KB 1.34KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 2.59KB 1.31KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (spec-report.js) 5.05KB 1.93KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 0.20KB 0.18KB
types (ui-action.js) 3.40KB 1.71KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

Copy link
Copy Markdown
Collaborator Author

✅ 验收(PM,session session_01GTRjn8xBqp75dk7kFupVRt)

实物核验:头 adb3f6ba5(两提交:修复 + 钉子桩具名类型化),5 文件 +276/−16;AiChatPage diff 全读 —— 一行修(planBuildingLabel 摘出 convZh 直读 UI 包)+ 在 convZh 定义处补承重注释(记录 #2632 漂移史与钉子位置);trailer 0;en 零值变更(drift 门绿)。
CI 终态(独立复核):20 检查全部 completed,18 success + 2 skipped,零失败。

裁定要点:

转 ready 并挂 auto-merge。


Generated by Claude Code

@yinlianghui
yinlianghui marked this pull request as ready for review August 9, 2026 01:03
@yinlianghui
yinlianghui added this pull request to the merge queue Aug 9, 2026
Merged via the queue into main with commit b3439f4 Aug 9, 2026
21 checks passed
@yinlianghui
yinlianghui deleted the claude/issue-3837-planbuilding-ui-locale branch August 9, 2026 01:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

2 participants