在 #3876(de 包 20 个值把德语开引号 „ 与 ASCII 直引号配成一对)落地时,全量扫 packages/i18n/src/locales/de.ts 的直引号残余,发现修完那 20 处之后还剩 6 个 U+0022,全在 approvalsInbox 一个命名空间里。
这是另一种缺陷形态,不是 #3876 的错配对:它们双侧都是 ASCII 直引号、自身是"配平"的,所以 #3876 落的持久不变量(每个 „ 之后第一个引号字符必须是 “)按设计扫不到它们 —— 这三个值里根本没有 „。
实测(main@2937bcf7d,packages/i18n/src/locales/de.ts)
3180: approveOneTitle: '„{{title}}“ genehmigen?', ← 正确的德语引号
3279: rejectOneTitle: '"{{title}}" ablehnen?', ← ASCII 双侧
3285: inlineApproved: '"{{title}}" genehmigt', ← ASCII 双侧
3286: inlineRejected: '"{{title}}" abgelehnt', ← ASCII 双侧
同一个审批收件箱里,批准的确认标题用 „…“、驳回的确认标题用 "…",而 inlineApproved / inlineRejected 这对孪生 toast 也是 ASCII。德语用户在同一屏、同一个操作对里看到两种引号排印。
en 侧两个兄弟是对称的(approveOneTitle: 'Approve "{{title}}"?' / rejectOneTitle: 'Reject "{{title}}"?',en 行 3401 / 3500),所以这不是 en 的形状差异带过来的,是 de 侧单侧翻译时的笔误。
#3876 修完后 de 包的计数:„ 45、“ 47、” 2、" 6 —— 这 6 个就是上面三个值(每值 2 个)。
修法
把这三个值的引号改成 „…“(U+201E … U+201C),与 3180 行的兄弟以及 de 包的多数派(45 处配对)一致。
改完 de 包的 U+0022 应为 0,届时可以把 #3876 的钉子升级成更强的形态(de 包零直引号),那比现在的显式清单更省事。
为什么门禁看不见
与 #3876 同一族的值域盲区:all-locales-key-parity 比键名与占位符形状、check-i18n-call-site-keys.mjs 只判 key 能否解析、check-i18n-en-drift.mjs 只在 en 值变化时要求九包跟随 —— 这三个值从落地起就是这样,不存在「变化」事件。
已有的钉子会拦住静默漂移
PR #3918(#3876)已在 packages/i18n/src/__tests__/de-quote-pairing-3876.test.ts 里把这 3 个 key 钉成显式清单:
const straight = DE.filter(([, v]) => v.includes(STRAIGHT)).map(([k]) => k);
expect(straight).toEqual([
'approvalsInbox.rejectOneTitle',
'approvalsInbox.inlineApproved',
'approvalsInbox.inlineRejected',
]);
所以数目再漂会红;修本单的人必须回来更新这份清单(或按上面说的直接换成「零直引号」断言)。
关联:#3876(错配对,PR #3918)、#3866(取证方法本身出错的先例)。
在 #3876(de 包 20 个值把德语开引号
„与 ASCII 直引号配成一对)落地时,全量扫packages/i18n/src/locales/de.ts的直引号残余,发现修完那 20 处之后还剩 6 个 U+0022,全在approvalsInbox一个命名空间里。这是另一种缺陷形态,不是 #3876 的错配对:它们双侧都是 ASCII 直引号、自身是"配平"的,所以 #3876 落的持久不变量(每个
„之后第一个引号字符必须是“)按设计扫不到它们 —— 这三个值里根本没有„。实测(main@2937bcf7d,
packages/i18n/src/locales/de.ts)同一个审批收件箱里,批准的确认标题用
„…“、驳回的确认标题用"…",而inlineApproved/inlineRejected这对孪生 toast 也是 ASCII。德语用户在同一屏、同一个操作对里看到两种引号排印。en 侧两个兄弟是对称的(
approveOneTitle: 'Approve "{{title}}"?'/rejectOneTitle: 'Reject "{{title}}"?',en 行 3401 / 3500),所以这不是 en 的形状差异带过来的,是 de 侧单侧翻译时的笔误。#3876 修完后 de 包的计数:
„45、“47、”2、"6 —— 这 6 个就是上面三个值(每值 2 个)。修法
把这三个值的引号改成
„…“(U+201E … U+201C),与 3180 行的兄弟以及 de 包的多数派(45 处配对)一致。改完 de 包的 U+0022 应为 0,届时可以把 #3876 的钉子升级成更强的形态(de 包零直引号),那比现在的显式清单更省事。
为什么门禁看不见
与 #3876 同一族的值域盲区:
all-locales-key-parity比键名与占位符形状、check-i18n-call-site-keys.mjs只判 key 能否解析、check-i18n-en-drift.mjs只在 en 值变化时要求九包跟随 —— 这三个值从落地起就是这样,不存在「变化」事件。已有的钉子会拦住静默漂移
PR #3918(#3876)已在
packages/i18n/src/__tests__/de-quote-pairing-3876.test.ts里把这 3 个 key 钉成显式清单:所以数目再漂会红;修本单的人必须回来更新这份清单(或按上面说的直接换成「零直引号」断言)。
关联:#3876(错配对,PR #3918)、#3866(取证方法本身出错的先例)。