Skip to content

fix(i18n): de approvalsInbox 三值用德语引号 „…“ 而非 ASCII 直引号 (#3919) - #3954

Merged
yinlianghui merged 3 commits into
mainfrom
claude/issue-3919-de-approvals-inbox-quotes
Aug 9, 2026
Merged

fix(i18n): de approvalsInbox 三值用德语引号 „…“ 而非 ASCII 直引号 (#3919)#3954
yinlianghui merged 3 commits into
mainfrom
claude/issue-3919-de-approvals-inbox-quotes

Conversation

@yinlianghui

@yinlianghui yinlianghui commented Aug 9, 2026

Copy link
Copy Markdown
Collaborator

Fixes #3919

改了什么

packages/i18n/src/locales/de.tsapprovalsInbox 三个值,标题两侧的 ASCII 直引号改成德语引号(U+201E 低位开引号、U+201C 闭引号):

key
approvalsInbox.rejectOneTitle "{{title}}" ablehnen? „{{title}}“ ablehnen?
approvalsInbox.inlineApproved "{{title}}" genehmigt „{{title}}“ genehmigt
approvalsInbox.inlineRejected "{{title}}" abgelehnt „{{title}}“ abgelehnt

同命名空间的兄弟 approveOneTitle 本来就是正确的 „{{title}}“ genehmigen?,所以德语审批者在同一屏、同一个操作对里看到两种引号排印:批准确认是德语引号,驳回确认和两个 inline toast 是打字机引号。„…“ 是 de 包自己的多数派(50 处配对)也是 DUDEN R11 的规定,这三个值是离群值。

只改值域排印 —— 键名不动、{{title}} 占位符不动(placeholder 形状是 all-locales-key-parity 比对的东西),en 侧按设计两侧都是 ASCII,一个字节没碰。

正文前提复核(与 issue 的差异)

issue 正文的行号取自 main@2937bcf7d;在本 PR 的基线 65bb513dc 上三个值仍在 3279 / 3285 / 3286,approveOneTitle 仍在 3180 —— 行号恰好未漂,值域缺陷与正文描述完全一致,前提成立。

一处需要写明的度量口径:正文说「改完 de 包的 U+0022 应为 0」。这指的是包内值的普查(#3876 钉子文件里 flatten(builtInLocales.de) 得到的 2914 个字符串叶子),不是 de.ts文件字节 —— 该文件本身有 5000 多个 ASCII 双引号在做字符串定界符(本仓无 prettier 配置,de.ts 的定界符风格本就混用双引号和单引号)。按值域口径,改后确为 0;这也正是钉子断言的口径。

钉子升级(#3876 的显式清单 → de 包零 U+0022)

de-quote-pairing-3876.test.ts 原来把这三个键钉成显式清单(expect(straight).toEqual([...三个键...]))。三个值修完后清单塌缩成更强的形态:de 包任何值都不含 U+0022

更强在于它同时覆盖两种缺陷形态,而且不需要逐键维护 —— 以后新落地的值带打字机引号会直接按键名翻红,没人需要回来编辑清单:

两条防退化措施:

  1. 反空转守卫。 toEqual([]) 恰好是「什么都没产出也会绿」的断言形状,所以谓词必须被证明仍能找到东西:en 包按设计用 ASCII 引号,同一个谓词在 en 上必须仍返回一长串(落地时 40 个值)。flatten 坏掉、包被清空或导出被改名都会让它归零,从而在这里翻红,而不是在上面一路绿。
  2. 四值逐字节钉子。 清零断言刻意不再点名任何键,所以新增一个 it 把 approvalsInbox 四值(批准/驳回确认标题 + 两个孪生 toast)整句钉住,并逐值断言 {{title}}„{{title}}“ 仍在 —— 一个什么都不点名的不变量会丢掉「这次改动动了什么」的记录。

包内计数 47 / 47 / 0 / " 6 → 50 / 50 / 0 / 0,文件里三处字面量(okSpans、计数恒等式、头注释普查)随之更新。#3876 自己的 20 键 / 22 span 普查表原样保留,不把后来的修复折进去。

反向验证(先预判方向,再跑)

预判:把 inlineRejected 单值临时改回 ASCII 引号,4 条断言翻红、#3876 的配对扫描器保持绿 —— 后者不是遗漏,正是本卡描述的结构盲区。

实测(9 个 test 里 4 红 5 绿)与预判逐条吻合:

变异已还原,还原后 de.ts 与变异前逐字节一致(diff 无输出),全套 34 文件 600 test 全绿。变异不提交。

消费半径清扫

三个键在全仓的引用面:十个 locale 包各自的翻译(未动其他包)、de-quote-pairing-3876.test.ts 的清单(本 PR 升级)、apps/console/src/pages/system/ApprovalsInboxPage.tsx 的三处 tr(key, en-fallback) 调用(fallback 是 en 字面量,不受影响)。另外两个提到 straight quote 的 i18n 测试(residue-namespaces-3546organization-namespace-3546)只涉及别的键,已逐个确认无关。

顺带核了一遍其他包这三个键是否也离群:ko / pt / es / ar 用 ASCII,而这四个包按 #3920 实测的裁定就是 ASCII;fr / ru 用 «…»、zh 用全角弯引号、ja 用 「」,都与各自裁定一致。只有 de 是离群值,没有需要另开单的兄弟缺陷。

一处刻意的小范围外改动(注释)

gridImportSavedMapping-i18n-3920.test.ts 头注释里那张按包统计 {{placeholder}} 包裹风格的普查表,de 行写着「ASCII 3」—— 那 3 个正是本 PR 修的三个值。改完该列归零,于是给表加了一句脚注说明,免得后来人去找三个已经不存在的 ASCII 值。纯注释,不动断言。

脚注里刻意不给新的 „…“ 计数:该表是在排除 #3920 移除的两处英文残留的前提下测得的,在当前包上重测是另一种口径 —— 实测重算得 43,而不是 38+3=41,差额正好是那两处已翻译的 grid.import 值。与其写一个口径混用的数字,不如只写已核实的部分。

验证

  • pnpm exec vitest run packages/i18n --maxWorkers=2 —— 34 文件 / 600 test 全绿
  • pnpm exec turbo run type-check --concurrency=2 —— 78/78 successful
  • node scripts/check-control-bytes.mjs —— OK(3850 个文件,零报告);另对改动文件自查了 gate 扫不到的控制字节范围,无命中
  • pnpm changeset:check —— fixed group / no-major 双绿
  • pnpm check:i18n-keys —— 2316/2316 字面量键可解析
  • pnpm check:i18n-drift —— 0 en 值变化(本 PR 没碰 en)
  • pnpm exec eslint 改动文件 —— 干净

changeset:.changeset/de-approvals-inbox-quotes-3919.md,@object-ui/i18n: patch(与 #3918 / #3936 两个同族 PR 一致)。

`approvalsInbox.rejectOneTitle` / `inlineApproved` / `inlineRejected` 三个值在
标题两侧都用 ASCII 直引号,而同一命名空间的兄弟 `approveOneTitle` 用的是正确的
德语引号(U+201E 开、U+201C 闭)。德语审批者因此在同一屏、同一个操作对里看到
两种引号排印:批准确认是 „…“,驳回确认与两个 inline toast 是 "…"。

`„…“` 是 de 包自己的多数派(50 处配对)也是 DUDEN R11 的规定,所以这三个值是
离群值。只改值域排印,键名与 `{{title}}` 占位符不动;en 侧按设计两侧都是 ASCII,
不受影响。

这与 #3876 是不同的缺陷形态:#3876 是德语开引号配了 ASCII 闭引号,而这三个值
两侧都是 ASCII、自身"配平",所以 #3876 留下的配对不变量结构上看不到它们 —— 那
个扫描器从每个 `„` 向前找第一个引号字符,而这三个值里根本没有 `„`。

de 包直引号清零后,把 `de-quote-pairing-3876.test.ts` 里的显式三键清单升级为更
强的形态:断言 de 包任何值都不含 U+0022。它同时覆盖两种缺陷形态,且不需要逐键
维护 —— 以后新落地的值带上打字机引号会直接按键名翻红。清零断言配了反空转守卫
(en 包同一谓词必须仍能找到 40 个值),避免"因为什么都没产出而绿"。同时新增
approvalsInbox 四值的逐字节钉子,因为清零断言本身不再点名任何键。

包内计数 47/47/0/6 → 50/50/0/0,三处字面量随之更新。
@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 4:56pm

Request Review

@github-actions github-actions Bot added the tests label Aug 9, 2026
claude added 2 commits August 9, 2026 16:54
那张按包统计 {{placeholder}} 包裹风格的普查表里,de 行的「ASCII 3」正是
approvalsInbox rejectOneTitle / inlineApproved / inlineRejected 这三个值。本
分支把它们德语化后该列归零,de 的裁定从 38 比 3 变为一致。

刻意不在此处给出新的 „…“ 计数:该表是在**排除** #3920 移除的两处英文残留的
前提下测得的,在当前包上重测是另一种口径,两者不可比(实测重算会得到 43 而非
38+3=41,差额正是那两处已翻译的 grid.import 值)。表保持原样作为当时裁定的
依据;活的不变量在 de-quote-pairing-3876.test.ts。
反空转守卫的注释写「see the it below」,但钉住 en 用 ASCII 引号的
`leaves en alone` 那条 it 在它上面。改为按名字点明。
@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-C_6f6JAA.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.64KB 106.39KB
core (index.js) 3.00KB 1.14KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 139.61KB 35.99KB
fields (index.js) 227.41KB 56.29KB
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) 118.02KB 30.47KB
plugin-designer (index.js) 210.51KB 42.51KB
plugin-detail (index.js) 236.63KB 59.02KB
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) 109.73KB 26.55KB
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) 20.15KB 6.72KB
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_01GTRjn8xBqp75dk7kFupVRt):通过,转 ready 并挂 auto-merge。#3919 落地。

核验记录(head 76f7ed461,基 65bb513dc,实物核验 + CI 亲读):

  1. 三值修正:rejectOneTitle / inlineApproved / inlineRejected 均已 „{{title}}“(实物 grep 确认),{{title}} 占位符保留;en 侧 ASCII 引号保持(不误伤)。
  2. 钉子升级论证成立:显式三键清单 → 全包「值层零 U+0022」断言,强于清单且免逐单维护;dev 对 toEqual([]) 的空断言风险配了防真空双护栏(DE.length > 2000 + 同谓词在 en 上须命中 20+),另加 approvalsInbox 四值逐字节钉。de 语言包 20 个值把德语开引号 „ 和 ASCII 直引号 " 配成一对(正确配对的只有 21 个),德语用户看到的收尾引号是错的 #3876 的配对不变量与普查表本体保留,计数字面量 47→50 如实更新。
  3. 反向验证:预判「单值回退 ASCII → 4 断言红、de 语言包 20 个值把德语开引号 „ 和 ASCII 直引号 " 配成一对(正确配对的只有 21 个),德语用户看到的收尾引号是错的 #3876 扫描器保持绿(结构盲区,正是本卡立卡理由)」,实跑逐条命中(4 failed | 5 passed);变异还原经 diff 确认未提交。
  4. 披露的两处注释级偏离(grid.import 已存映射那 5 个键在 9 个非 en 包里有 7 个原样留着英文(只有 zh/ja 译了)—— 德/法/西/葡/俄/韩/阿用户在导入弹窗看到英文 #3920 普查表注脚、交叉引用修正)均为改动自身的引用面,处置得当;dev 拒绝发布单位混算的重计数、只写已验证部分 —— 正确。
  5. 门与规程:i18n 600 测试全绿、2316 键全解析、en 零漂移;type-check 78/78;控制字节门过;changeset patch(随同族 fix(i18n): de 包 20 个值把德语开引号 „ 配到 ASCII 直引号,收尾引号改回 „…“ (#3876) #3918/fix(i18n): grid.import 已存映射五键在 ko/de/fr/es/pt/ru/ar 补译(#3920) #3936 先例);提交文案控制词 0;⛔ releases/ 未触碰。
  6. CI 亲读终态:20/20 全 completed、0 失败(Test shard×4 至 17:02:07Z、Type Check 17:02:30Z、Lint 16:59:53Z;coverage/dependabot path-filter skipped 计绿)。dev 交报告时如实标注 6 项未终态而非妄称绿 —— 按纪律由 PM 亲读补齐终态。

注意事项转告维护者(dev concern,合理保留):零直引号钉现在治理整个 de 包 —— 未来任何 de 值合法需要直引号(代码样例/CLI flag)会大声红,届时按需放行。


Generated by Claude Code

@yinlianghui
yinlianghui marked this pull request as ready for review August 9, 2026 17:05
@yinlianghui
yinlianghui added this pull request to the merge queue Aug 9, 2026
Merged via the queue into main with commit 69becd2 Aug 9, 2026
21 checks passed
@yinlianghui
yinlianghui deleted the claude/issue-3919-de-approvals-inbox-quotes branch August 9, 2026 17:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

2 participants