PR:i18n settings 拆分与翻译质量门禁(含 messages no-emoji)#588
Conversation
…n/claude-code-hub into refactor/i18n-split-settings
# Conflicts: # messages/en/settings.json # messages/ja/settings.json # messages/ru/settings.json # messages/zh-CN/settings.json # messages/zh-TW/settings.json
|
Important Review skippedToo many files! 136 files out of 286 files are above the max files limit of 150. You can disable this status message by setting the ✨ Finishing touches🧪 Generate unit tests (beta)
Comment |
|
Too many files changed for review. ( |
Summary of ChangesHello @YangQing-Lin, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! 本次拉取请求旨在全面提升项目的国际化(i18n)管理水平,通过对 Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
| function loadJson(p) { | ||
| return JSON.parse(fs.readFileSync(p, "utf8")); | ||
| } | ||
|
|
||
| function normalizeLocales(messagesRoot, locales) { | ||
| if (typeof locales === "string") return normalizeLocales(messagesRoot, [locales]); | ||
| if (Array.isArray(locales) && locales.length > 0) { | ||
| return locales | ||
| .flatMap((s) => String(s).split(",")) | ||
| .map((s) => s.trim()) | ||
| .filter(Boolean); | ||
| } | ||
|
|
||
| const dirs = fs.readdirSync(messagesRoot, { withFileTypes: true }); | ||
| return dirs | ||
| .filter((d) => d.isDirectory() && !d.name.startsWith(".")) | ||
| .map((d) => d.name) | ||
| .sort((a, b) => a.localeCompare(b)); | ||
| } | ||
|
|
||
| function toCodepoint(cp) { | ||
| const hex = cp.toString(16).toUpperCase(); | ||
| return `U+${hex.padStart(4, "0")}`; | ||
| } | ||
|
|
||
| function listEmojiCodepoints(value) { | ||
| EMOJI_RE.lastIndex = 0; | ||
| const out = []; | ||
| for (const m of value.matchAll(EMOJI_RE)) { | ||
| const cp = m[0].codePointAt(0); | ||
| if (typeof cp === "number") out.push(toCodepoint(cp)); | ||
| } | ||
| return out; | ||
| } | ||
|
|
||
| function findMessagesEmojiMatches({ messagesDir, locales }) { | ||
| const root = messagesDir || path.join(process.cwd(), "messages"); | ||
| const targets = normalizeLocales(root, locales); | ||
| const rows = []; | ||
|
|
||
| for (const locale of targets) { | ||
| const localeDir = path.join(root, locale); | ||
| if (!fs.existsSync(localeDir) || !fs.statSync(localeDir).isDirectory()) continue; | ||
|
|
||
| const files = emojiAudit.listJsonFiles(localeDir); | ||
| for (const file of files) { | ||
| const relFileNative = path.relative(localeDir, file); | ||
| const relFilePosix = relFileNative.replaceAll(path.sep, "/"); | ||
| const keyPrefix = emojiAudit.fileToKeyPrefix(relFileNative); | ||
| const obj = loadJson(file); | ||
|
|
||
| for (const leaf of emojiAudit.flattenLeafStrings(obj)) { | ||
| if (typeof leaf.value !== "string") continue; | ||
| const emojiCount = emojiAudit.countEmojiCodepoints(leaf.value); | ||
| if (emojiCount === 0) continue; | ||
|
|
||
| const fullKey = keyPrefix | ||
| ? leaf.key | ||
| ? `${keyPrefix}.${leaf.key}` | ||
| : keyPrefix | ||
| : leaf.key; | ||
|
|
||
| const codepoints = Array.from(new Set(listEmojiCodepoints(leaf.value))).sort((a, b) => | ||
| a.localeCompare(b) | ||
| ); | ||
|
|
||
| rows.push({ | ||
| file: path.posix.join("messages", locale, relFilePosix), | ||
| key: fullKey, | ||
| emojiCount, | ||
| codepoints, | ||
| }); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return rows.sort((a, b) => a.file.localeCompare(b.file) || a.key.localeCompare(b.key)); | ||
| } |
There was a problem hiding this comment.
这个 PR 引入了多个新的 i18n 审计脚本,例如 audit-messages-no-emoji.js、audit-messages-emoji.js 和 audit-settings-placeholders.js。这些脚本极大地提升了翻译质量的保障。
我注意到这些脚本之间存在一些重复的代码,主要是一些辅助函数和文件遍历逻辑。例如:
normalizeLocalesloadJsonlistJsonFilesflattenLeafStrings/flatten- 遍历
messages目录下各 locale 和 JSON 文件的逻辑
为了提高代码的可维护性并遵循 DRY (Don't Repeat Yourself) 原则,建议将这些通用的辅助函数和遍历逻辑提取到一个共享的工具文件中(例如 scripts/i18n-utils.js)。
这样可以:
- 减少重复代码:避免在多个文件中维护相同的逻辑。
- 简化未来更新:当需要修改 locale 的发现逻辑或文件处理方式时,只需在一个地方修改。
- 提高脚本清晰度:每个审计脚本可以更专注于其核心的检查逻辑,而不是文件系统操作。
例如,可以创建一个通用的 forEachMessageLeaf 函数,它接收一个回调函数,在每个消息的叶子节点上执行特定的检查。
这是一个重构建议,可以使这套优秀的审计工具更加健壮和易于维护。
scripts/audit-messages-no-emoji.js
Outdated
| const path = require("node:path"); | ||
| const emojiAudit = require("./audit-messages-emoji.js"); | ||
|
|
||
| const EMOJI_RE = /\p{Extended_Pictographic}/gu; |
There was a problem hiding this comment.
[High] [LOGIC-BUG] Emoji audit misses keycap/flag sequences (false negatives)
Why this is a problem: scripts/audit-messages-no-emoji.js:5 uses const EMOJI_RE = /\p{Extended_Pictographic}/gu;, which does not match common emoji sequences like keycaps ([0-9#*]\uFE0F?\u20E3) or flags (\p{Regional_Indicator}{2}). This means bun run i18n:audit-messages-no-emoji:fail can report OK even when messages/**/*.json contains those emoji.
Suggested fix:
// Handle Extended_Pictographic + keycap + flag sequences
const EMOJI_RE =
/(\p{Extended_Pictographic}|\p{Regional_Indicator}{2}|[0-9#*]\uFE0F?\u20E3)/gu;
// When listing codepoints, output full sequences (no emoji printed)
for (const m of value.matchAll(EMOJI_RE)) {
const seq = Array.from(m[0])
.map((ch) => ch.codePointAt(0))
.filter((cp) => typeof cp === "number")
.map((cp) => toCodepoint(cp))
.join("+");
if (seq) out.push(seq);
}Also update scripts/audit-messages-emoji.js:4 so maskEmoji() / countEmojiCodepoints() stay consistent, and add a unit test case that includes a keycap and a flag sequence.
There was a problem hiding this comment.
Code Review Summary
This PR is XL and spans messages content, new auditing scripts, new unit tests, and documentation updates, which increases merge risk and makes regression isolation difficult. One correctness gap was found in the no-emoji gate: the current emoji regex can miss keycap and flag sequences, allowing false negatives.
PR Size: XL
- Lines changed: 25126
- Files changed: 286
- Split suggestion: Split into separate PRs for (1) settings messages split + runtime loader, (2) audit scripts + tests, (3) translation content updates (messages JSON only), (4) docs/checklists.
Issues Found
| Category | Critical | High | Medium | Low |
|---|---|---|---|---|
| Logic/Bugs | 0 | 1 | 0 | 0 |
| Security | 0 | 0 | 0 | 0 |
| Error Handling | 0 | 0 | 0 | 0 |
| Types | 0 | 0 | 0 | 0 |
| Comments/Docs | 0 | 0 | 0 | 0 |
| Tests | 0 | 0 | 0 | 0 |
| Simplification | 0 | 0 | 0 | 0 |
Critical Issues (Must Fix)
- None.
High Priority Issues (Should Fix)
- (Confidence 90)
scripts/audit-messages-no-emoji.jsmisses keycap/flag emoji sequences due to\p{Extended_Pictographic}only, weakening the intended no-emoji fail gate.
Review Coverage
- Logic and correctness
- Security (OWASP Top 10)
- Error handling
- Type safety
- Documentation accuracy
- Test coverage
- Code clarity
Automated review by Codex AI
There was a problem hiding this comment.
- Identified PR
#588and applied the labelsize/XL(25126 lines changed across 286 files). - Posted an inline review comment on
scripts/audit-messages-no-emoji.js:5about the emoji regex missing keycap/flag sequences (false negatives), with a concrete regex + test suggestion. - Submitted the full review summary to the PR via
gh pr review.
There was a problem hiding this comment.
Code Review Summary
This is a well-structured large PR that implements i18n settings splitting and translation quality gates. The code is clean, consistent across all locales, and includes comprehensive test coverage. No significant issues were identified.
PR Size: XL
- Lines changed: 25,126 (+14,033 / -11,093)
- Files changed: 288
Note for XL PRs: This PR makes a coordinated architectural change (i18n settings split), so while large, the changes are cohesive and would be difficult to split further without breaking the migration.
Issues Found
| Category | Critical | High | Medium | Low |
|---|---|---|---|---|
| Logic/Bugs | 0 | 0 | 0 | 0 |
| Security | 0 | 0 | 0 | 0 |
| Error Handling | 0 | 0 | 0 | 0 |
| Types | 0 | 0 | 0 | 0 |
| Comments/Docs | 0 | 0 | 0 | 0 |
| Tests | 0 | 0 | 0 | 0 |
| Simplification | 0 | 0 | 0 | 0 |
Key Observations
Strengths identified (not listed as praise, but context):
- All
messages/<locale>/settings/index.tsfiles are byte-for-byte consistent in structure - New audit scripts (
audit-messages-emoji.js,audit-messages-no-emoji.js,audit-settings-placeholders.js,sync-settings-keys.js) have proper module exports for testability - Test files cover edge cases including allowlist filtering, CLI flags, and output formats
- Emoji removal from JSON files follows the correct pattern (replacing with text equivalents)
Review Coverage
- Logic and correctness - Clean
- Security (OWASP Top 10) - Clean
- Error handling - Appropriate for build scripts (exceptions propagate to terminate)
- Type safety - Clean (TypeScript index files properly typed)
- Documentation accuracy - Clean
- Test coverage - Adequate (14 new test files covering scripts and guards)
- Code clarity - Good
Automated review by Claude AI
* PR: 修复 Edge Runtime 下 `process.once` 构建告警(AsyncTaskManager 导入链) (#589) * fix: skip async task manager init on edge * fix: avoid static async task manager import * test: cover edge runtime task scheduling * chore: document edge runtime process.once fix * chore: record edge runtime warning baseline * fix: drop NEXT_PHASE and lazy-init async task manager * test: isolate NEXT_RUNTIME in cloud price sync tests * docs: stabilize edge process.once repro baseline * docs: make rollback instructions hashless * docs: add grep checklist for edge warning audit * chore: run regression gate and align docs * test: cover edge runtime guard on register * Update src/lib/async-task-manager.ts 补充 NEXT_PHASE === "phase-production-build" 检查 Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * chore: format code (fix-edge-runtime-process-once-bee7e19) * PR:i18n settings 拆分与翻译质量门禁 (#588) * refactor(i18n): split settings json into smaller files * refactor(i18n): load settings from split module * refactor(i18n): remove legacy settings.json * chore(i18n): update sync-settings-keys for split layout * test(i18n): add split settings guards * chore: align biome schema version * chore(i18n): document messages loading contract * chore(i18n): add settings split verification notes * chore: format code (refactor-i18n-split-settings-3f48fec) * chore: fix i18n request formatting * chore: format code (refactor-i18n-split-settings-a1eff62) * fix: replace settings placeholder translations * chore: verify settings sync script is idempotent * test: run i18n settings split guards * test: add audit for zh-CN placeholder settings strings * chore: apply biome formatting * chore: document manual i18n settings verification * fix: translate all providers filter in ja * fix: translate all providers filter in zh-TW * fix: translate providers section copy in zh-TW * fix: translate providers section copy in ja * feat: extend placeholder audit output * feat: add allowlist for placeholder audit * docs: define i18n translation quality rules * chore: add i18n audit fail commands * docs: add i18n PR checklist * chore: format i18n audit tests * fix: translate dashboard placeholders * fix: translate myUsage placeholders * fix: enforce locale-specific parentheses * fix: start translating provider form strings * fix: translate provider form strings * fix: translate provider guide content * test: add ja dashboard parentheses guard * test: add zh-TW dashboard parentheses guard * test: add zh-TW myUsage parentheses guard * chore: translate ja provider form strings * chore: translate zh-TW provider form strings * chore: translate ja providers guide * chore: translate zh-TW providers guide * chore: refine zh-TW dashboard strings * chore: translate ja providers strings * chore: translate zh-TW providers strings * chore: refine zh-TW api test strings * chore: translate zh-TW settings small modules * chore: translate ja settings small modules * chore: clear i18n placeholders in settings * chore: format code (refactor-i18n-split-settings-2437d19) * test: fix biome formatting in i18n test * chore: verify Biome lint gate (I18NE-030) * chore: add messages emoji audit script (I18NE-010) * fix: remove emoji from messages warnings (I18NE-040) * test: add messages no-emoji audit gate (I18NE-050) * docs: add zh-CN i18n docs (I18NE-020) * docs: add messages no-emoji rule (I18NE-060) * chore: run full regression checks (I18NE-070) * docs: add i18n PR evidence template (I18NE-080) * fix: make messages no-emoji audit path-sep safe * docs: add bun alias for messages no-emoji audit * fix: detect keycap and flag emoji sequences in i18n message audits * fix(provider): allow removing custom whitelisted models (#592) (#593) * fix(rectifier): detect 'signature: Field required' error and trigger rectifier (#594) - Extend detectThinkingSignatureRectifierTrigger to match 'signature: Field required' - Add Rule 72 for friendly error message when signature field is missing - Add comprehensive test cases for the new detection logic This fixes an issue where switching from non-Anthropic to Anthropic channels with thinking blocks missing signature fields would fail without proper handling. * feat(users): increase provider group length to 200 (#591) close #590 * feat(usage-doc): update OpenCode config example with GPT-5.2 and Gemini v1beta (#597) - Add OpenAI GPT-5.2 model configuration with reasoningEffort options - Add GPT-5.2-small variant using medium reasoning effort - Fix Gemini baseURL to use /v1beta endpoint - Update i18n strings to reflect different baseURLs per provider * feat: auto-complete Codex session identifiers (#599) * fix: Codex session completion must not inject metadata (#601) * feat: auto-complete Codex session identifiers * fix: avoid Codex metadata injection --------- Co-authored-by: YangQing-Lin <56943790+YangQing-Lin@users.noreply.github.com> Co-authored-by: Hwwwww-dev <47653238+Hwwwww-dev@users.noreply.github.com>
背景与目标
本 PR 以
settings / dashboard / myUsage为主范围,完成 i18n 的结构拆分、翻译质量工程化门禁与多语言内容补齐,并补充中文对照文档与 PR 流程清单。当前分支已合并最新dev,并处理了与 settings split 相关的冲突点,保证可回归。主要变更
1) settings messages 拆分(替代 legacy settings.json)
messages/<locale>/settings.json,改为messages/<locale>/settings/目录下小模块 JSON(递归结构)。messages/<locale>/settings/index.ts组装settingsnamespace,并由messages/<locale>/index.ts引入,保证 key path 稳定。scripts/sync-settings-keys.js支持 legacy 与 split 两种布局,使用zh-CN作为 canonical,同步所有 locale 的 key 集合并填充缺失项。2) 翻译质量规则落地(R1/R2/R3/R4)与可执行门禁
docs/i18n-translation-quality.md(并提供中文对照版),定义 scope 与可执行规则:{count})。messages/**/*.json禁止 emoji(输出 codepoints,不直接打印 emoji 字符)。scripts/audit-settings-placeholders.js:支持--scope=settings,dashboard,myUsage、TSV/JSON 输出与--fail。scripts/audit-messages-no-emoji.js:可作为 CI fail gate(--fail),并修复跨平台路径分隔符问题。scripts/audit-messages-emoji.js:用于清理前的命中清单与复现证据(mask 输出)。package.jsonscripts):bun run i18n:audit-placeholders/bun run i18n:audit-placeholders:failbun run i18n:audit-messages-no-emoji/bun run i18n:audit-messages-no-emoji:fail3) 翻译内容补齐与一致性修复(重点 ja / zh-TW)
messages/*/settings/prices.json等新增模块的翻译细节,确保通过 R1 与括号规范相关测试。4) messages 去 emoji + 回归门禁
messages/**中的 emoji(不改 key/结构,仅改 value 文案),例如provider-chain.json与settings/data.json的 warning/info 文案。5) 文档与协作流程补强(含中文对照)
docs/i18n-pr-checklist.md(及中文对照版):可执行命令 + 人工抽查最小集。docs/i18n-settings-split.md(及中文对照版):拆分结构与验证方式。docs/i18n-pr-evidence-2026-01-11.md:PR 证据模板(可复制到 PR 描述)。CONTRIBUTING.md补充 i18n 变更入口,方便贡献者按规则执行。6) 同步最新 dev(已合并并解决冲突)
dev,并在冲突点上保持 settings split 的结构一致性:messages/*/settings/prices.json)。settings.json)。验证与门禁(本地已通过)
bun run lintbun run typecheckbun run testbun run buildbun run i18n:audit-placeholders:failbun run i18n:audit-messages-no-emoji:fail影响范围与迁移说明
messages/<locale>/settings/**.json;不要再编辑messages/<locale>/settings.json(已移除)。node scripts/sync-settings-keys.js保证各 locale key 集一致;bun run i18n:audit-placeholders/:fail检查占位候选;bun run i18n:audit-messages-no-emoji:fail避免 messages emoji 回归。风险与建议抽查
docs/i18n-pr-checklist.md对ja与zh-TW的settings/dashboard/myUsage做最小人工 spot-check(截图/路径留证)。bun run build通过,但 Next/Turbopack 对部分 Edge Runtime 路径存在 Node.js API 警告(不影响本 PR 目标;如需后续治理可单独开任务)。