Files
2026Technology-Competition/docs/superpowers/specs/2026-07-26-i18n-design.md
T

423 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# i18n 国际化 — Extension Design Spec
## 1. 概述
为 vscode-code-reviewer 实现运行时动态语言切换。用户在设置面板选择语言后,所有 webview 面板、通知、QuickPick、报告等 UI 文字立即生效,无需重载 VS Code 窗口。
语言源:复用现有 `ai.outputLanguage` 配置项(`zh-CN` / `en` / `ja`)。
### 范围
| 区域 | 是否纳入 | 说明 |
|------|---------|------|
| Webview 面板(设置/审查报告/导入预览) | 是 | 动态重渲染 |
| 通知(showInformationMessage 等) | 是 | 下次调用用新语言 |
| QuickPick | 是 | 下次调用用新语言 |
| 进度条(withProgress | 是 | 下次调用用新语言 |
| 报告导出(Markdown) | 是 | 导出时取当前语言 |
| 适配器错误消息 | 是 | 每次调用取当前语言 |
| `package.json` contributes | 否 | 跟随 VS Code 机制,不影响 |
| AI 提示词(engine.ts / fixer.ts | 否 | 保持中文,保证 AI 输出质量稳定 |
---
## 2. 核心模块:`src/i18n/messages.ts`
### 2.1 类型定义
```typescript
export type Language = 'zh-CN' | 'en' | 'ja';
const defaultLang: Language = 'zh-CN';
```
### 2.2 消息注册
单文件注册所有文案,每条包含三种语言的值。命名采用 `模块.场景.含义`,点分隔,全小写。
```typescript
const messages = {
'review.noEditor': {
'zh-CN': '请先打开一个文件',
en: 'Please open a file first',
ja: '最初にファイルを開いてください',
},
// ...
};
类型约束:`Record<string, Record<Language, string>>`,确保所有消息三种语言都有值。
### 2.3 运行时语言
```typescript
let currentLang: Language = defaultLang;
```
### 2.4 `t()` 函数
```typescript
export function t(key: string): string {
const msg = messages[key]?.[currentLang] ?? messages[key]?.[defaultLang];
return msg ?? key;
}
```
插件内所有文案都通过 `t()` 获取,不再硬编码。无模板插值参数,简单直接。
### 2.5 语言切换
```typescript
const languageChangeEmitter = new vscode.EventEmitter<Language>();
export function setLanguage(lang: Language): void {
if (currentLang === lang) return;
currentLang = lang;
languageChangeEmitter.fire(lang);
}
export function onLanguageChange(listener: (lang: Language) => void): vscode.Disposable {
return languageChangeEmitter.event(listener);
}
export function getLanguage(): Language {
return currentLang;
}
```
---
## 3. 语言切换流程
采用"写配置 → 事件驱动"的单一入口模式:
```
用户在设置面板改语言下拉
webview postMessage 'setLanguage'
extension 端 handleMessage:
vscode.workspace.getConfiguration('vscode-code-reviewer')
.update('ai.outputLanguage', lang, true)
onDidChangeConfiguration 监听到变化(ext.activate 中注册)
if (changed.key === 'vscode-code-reviewer.ai.outputLanguage')
setLanguage(newLang)
onLanguageChange 触发
所有 webview 回调: postMessage 推送新翻译 → 全量重渲染
所有通知/QuickPick: 下次调用时 t() 取新语言
```
### 3.1 为什么不用同步 setLanguage + update
- 用户可能通过 VS Code 原生设置界面改语言,`onDidChangeConfiguration` 能自动捕获
- 单一入口、无分支、逻辑内聚
### 3.2 初始化
`extension.ts``activate()` 中:
```typescript
// 从当前配置初始化语言
const lang = getAIOutputLanguage() as Language;
setLanguage(lang);
// 监听配置变更
context.subscriptions.push(
vscode.workspace.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('vscode-code-reviewer.ai.outputLanguage')) {
const newLang = getAIOutputLanguage() as Language;
setLanguage(newLang);
}
})
);
```
---
## 4. Webview 刷新策略
### 4.1 消息推送
扩展端构建翻译后的消息 map 通过 `postMessage` 发给 webview
```typescript
// 只推送当前 webview 需要的 key
function pushLanguage(webview: vscode.Webview) {
const translated: Record<string, string> = {};
for (const key of webviewI18nKeys) {
translated[key] = t(key);
}
webview.postMessage({ type: 'lang', lang: currentLang, messages: translated });
}
```
### 4.2 Webview 端处理
```javascript
window.addEventListener('message', e => {
if (e.data.type === 'lang') {
render(e.data.messages, e.data.lang);
}
});
```
### 4.3 重渲染
全量重渲染。每个 webview 在收到 `lang` 消息后重建 HTML。
设置面板特殊处理:
- 保存表单当前值到 JS 变量中
- 语言下拉变化时:先持久化当前表单值(API Key 等),再改语言,最后重建
- 重建后用保存的变量回填表单
### 4.4 Webview HTML 生成方式
当前 webview 在扩展端生成 HTML 字符串。改造后:
- 被翻译的文案在生成 HTML 时用 `t()` 注入(扩展端调用)
- 生成完整 HTML 时文案已经翻译好,webview 端 JS 无需再翻译
- webview 端 JS 只是接收到 `lang` 消息时重新请求全集 HTML(或接受新 HTML 字符串替换)
三种面板处理方式:
| 面板 | 策略 |
|------|------|
| 审查报告 | 重新生成 HTML → postMessage → 替换 `document.body.innerHTML` |
| 设置面板 | 保留表单 JS 变量,重新生成 HTML 后回填 |
| 导入预览 | 重新生成 HTML → postMessage → 替换 |
---
## 5. 消息清单设计
预估约 60 条文案,按命名空间分组。
### 5.1 review.* (审查类)
| Key | zh-CN | en (参考) |
|-----|-------|-----------|
| `review.noEditor` | 请先打开一个文件 | Please open a file first |
| `review.running` | 正在审查... | Reviewing... |
| `review.staticAnalysis` | 运行静态分析... | Running static analysis... |
| `review.aiReview` | 运行 AI 审查... | Running AI review... |
| `review.completeSelection` | 选中代码审查完成: {0} 个问题 | Selection review: {0} issues |
| `review.needRunFirst` | 请先运行完整审查生成报告 | Run a full review first |
| `review.noSelection` | 请先选中要审查的代码 | Select code to review first |
| `review.needApiKey` | 请先在设置面板中配置 API Key | Configure API Key in Setup first |
| `review.fixNotAvailable` | 单条修复功能开发中 | Single fix under development |
| `review.fixAllNotAvailable` | 批量修复功能开发中 | Batch fix under development |
### 5.2 export.* (导出类)
| Key | Description |
|-----|-------------|
| `export.selectMethod` | 选择导出方式 |
| `export.copyToClipboard` | 复制到剪贴板 |
| `export.downloadMarkdown` | 下载 Markdown 文件 |
| `export.copied` | 报告已复制到剪贴板 |
| `export.saveDialogTitle` | 保存审查报告 |
| `export.saved` | 报告已保存到 {0} |
### 5.3 setup.* (设置面板)
| Key | Description |
|-----|-------------|
| `setup.header` | 净码特工 · 代码审查 · 设置 |
| `setup.quickStart` | 快速开始 |
| `setup.gettingStarted` | 三步启用代码审核 |
| `setup.step1` | 安装插件后,配置 AI 模型及 API Key... |
| `setup.step2` | 启用自定义规则,补充团队特有的编码规范 |
| `setup.step3` | 保存并测试连接,验证配置无误后即可触发审核 |
| `setup.step3Hint` | 按 Ctrl + Shift + R 快捷键触发审核... |
| `setup.engineSection` | 审核引擎 |
| `setup.commonRules` | 共通规则 |
| `setup.linterStatic` | Linter 静态分析 |
| `setup.customRules` | 自定义规则 |
| `setup.teamCoding` | 团队编码规范 |
| `setup.aiReview` | AI 审核 |
| `setup.deepReview` | 深度代码审查 |
| `setup.aiConfig` | AI 模型配置 |
| `setup.provider` | 模型提供商 |
| `setup.notConfigured` | 未配置 |
| `setup.model` | 模型名称 |
| `setup.modelHint` | 建议使用支持结构化输出的模型 |
| `setup.apiKey` | API Key |
| `setup.baseUrl` | Base URL |
| `setup.keyStorageHint` | Key 仅存储在本地 VS Code 安全存储中 |
| `setup.outputLang` | 输出语言 |
| `setup.outputLangHint` | AI 审查结果输出语言 |
| `setup.customRulesSection` | 自定义规则 |
| `setup.ruleList` | 规则列表 |
| `setup.ruleCount` | {0} 条 |
| `setup.ruleNamePlaceholder` | 输入规则名称... |
| `setup.add` | + 添加 |
| `setup.ruleNameHint` | 建议使用英文名称... |
| `setup.reset` | 重置 |
| `setup.saveAndTest` | 保存并测试连接 |
| `setup.testSuccess` | ✓ 连接成功 |
| `setup.testFail` | ✗ 连接失败: {0} |
| `setup.setApiKeyFirst` | 请先设置 API Key |
| `setup.setBaseUrlFirst` | 请先设置 Base URL |
| `setup.selectRuleFile` | 选择规则文件 |
| `setup.fileExists` | 文件 {0} 已存在 |
| `setup.importSuccess` | 规则文件已导入: {0} |
| `setup.importCancelled` | 导入已取消 |
| `setup.importDedupResult` | 规则已导入: ... ({0} 条,{1} 条重复已注释,{2} 条重叠已标注) |
| `setup.importFail` | 规则导入失败: {0} |
| `setup.openSetupFail` | 无法打开设置面板 |
| `setup.openSettingsJson` | 打开设置 (JSON) |
| `setup.noWorkspace` | 请先打开工作区 |
| `setup.manageRulesHint` | 请在设置面板中管理自定义规则 |
| `setup.languageLabels` | 中文(简体)/ English / 日本語 |
### 5.4 report.* (审查报告)
| Key | Description |
|-----|-------------|
| `report.title` | 代码审查报告 |
| `report.panelTitle` | 净码特工 · 代码审查报告 |
| `report.totalIssues` | 总计问题 |
| `report.errors` | 错误 |
| `report.warnings` | 警告 |
| `report.info` | 建议 |
| `report.fixAll` | 全部修复 |
| `report.rerun` | 重新审查 |
| `report.export` | 导出报告 |
| `report.sourceLinter` | Linter |
| `report.sourceCustom` | 自定义 |
| `report.sourceAI` | AI |
| `report.noIssues` | 未发现任何问题 |
| `report.noRuleViolations` | 未发现规则违规 |
| `report.noAIFindings` | 无 AI 审查建议 |
| `report.skipCustomRules` | 当前文件语言无匹配的自定义规则 |
| `report.executionErrors` | 执行错误 |
| `report.degraded` | AI 审查未完成,报告仅包含部分结果 |
| `report.degradedBanner` | 部分 AI 功能不可用,报告已降级 |
| `report.file` | 文件 |
| `report.language` | 语言 |
| `report.duration` | 耗时 |
| `report.tools` | 分析工具 |
| `report.totalSummary` | 总计: {0} \| 错误: {1} \| 警告: {2} \| 建议: {3} |
| `report.staticSection` | 静态分析 · {0} 个问题 |
| `report.customSection` | 自定义规则 · {0} 个问题 |
| `report.aiSection` | AI 审查 · {0} 条建议 |
| `report.suggestion` | 建议 |
| `report.noProblems` | 未发现问题 |
### 5.5 import.* (规则导入)
| Key | Description |
|-----|-------------|
| `import.previewTitle` | 规则导入预览 |
| `import.source` | 来源:{0} · 检测到 {1} 条规则 |
| `import.fullDuplicate` | 完全重复 {0} 条 |
| `import.partialOverlap` | 部分重叠 {0} 条 |
| `import.noDuplicate` | 无重复 {0} 条 |
| `import.willKeep` | 将保留 {0} 条规则,注释 {1} 条规则 |
| `import.keep` | 保留 |
| `import.comment` | 注释 |
| `import.cancel` | 取消 |
| `import.confirm` | 确认导入 |
| `import.emptyFile` | 所选文件为空 |
| `import.needApiKey` | 请先在设置面板中配置 API Key |
| `import.timeout` | AI 生成规则超时,请检查网络或增大 ai.timeout 配置 |
| `import.aiFail` | AI 生成规则失败: {0} |
| `import.emptyResponse` | AI 返回内容为空 |
| `import.excelReadFail` | 读取 Excel 文件失败: {0} |
| `import.excelEmpty` | Excel 文件没有工作表 |
| `import.excelNoData` | Excel 工作表中没有数据 |
| `import.docxReadFail` | 读取 Word 文件失败: {0} |
| `import.docxEmpty` | Word 文件中没有可提取的文本内容 |
| `import.pptxReadFail` | 读取 PowerPoint 文件失败: {0} |
| `import.pptxEmpty` | PowerPoint 文件中没有可提取的文本内容 |
### 5.6 adapter.* (适配器错误)
| Key | Description |
|-----|-------------|
| `adapter.javaNotInstalled` | Java 11+ 未安装或不在 PATH 中 |
| `adapter.sqlfluffNotInstalled` | sqlfluff 未安装,请执行 pip install sqlfluff |
| `adapter.invalidApiKey` | API Key 无效,请重新设置 |
| `adapter.noApiKey` | 未配置 API Key |
| `adapter.createProviderFail` | 创建 Provider 失败: {0} |
| `adapter.customRuleParseFail` | 自定义规则响应解析失败: {0} |
| `adapter.customRuleRequestFail` | 自定义规则请求失败: {0} |
| `adapter.aiReviewParseFail` | AI 审查响应解析失败: {0} |
| `adapter.aiReviewRequestFail` | AI 审查请求失败: {0} |
### 5.7 extension.* (插件级)
| Key | Description |
|-----|-------------|
| `extension.activated` | 净码特工 · Code Purifier 已激活 |
### 5.8 命令标题(仅作参考,不修改 package.json
命令标题在 `package.json` 保持中文不变,不纳入本次范围。
---
## 6. 文件变更清单
| 变更类型 | 文件 | 说明 |
|----------|------|------|
| 新增 | `src/i18n/messages.ts` | 核心 i18n 模块 |
| 新增 | `src/i18n/messages.test.ts` | 消息完整性测试 |
| 修改 | `src/extension.ts` | activate 中初始化语言 + 监听配置变更 |
| 修改 | `src/activation/commands.ts` | 通知/进度条/QuickPick 文案替换为 t() |
| 修改 | `src/views/setupView.ts` | 设置面板 HTML 生成用 t(),语言下拉即时生效 |
| 修改 | `src/panel/webview.ts` | 审查报告 HTML 生成用 t(),监听语言变化重渲染 |
| 修改 | `src/views/import-preview.ts` | 导入预览 HTML 用 t() |
| 修改 | `src/utils/report.ts` | 报告生成用 t() |
| 修改 | `src/adapters/pmd.ts` | 错误消息替换 |
| 修改 | `src/adapters/sql-lint.ts` | 错误消息替换 |
| 修改 | `src/ai/engine.ts` | 错误消息替换(提示词不修改) |
| 修改 | `src/ai/providers/openai-compatible.ts` | 错误消息替换 |
| 修改 | `src/rules/import-service.ts` | 错误消息替换 |
| 修改 | `src/rules/converters/excel-converter.ts` | 错误消息替换 |
| 修改 | `src/rules/converters/docx-converter.ts` | 错误消息替换 |
| 修改 | `src/rules/converters/pptx-converter.ts` | 错误消息替换 |
---
## 7. 实现顺序
1. 创建 `src/i18n/messages.ts`(核心模块 + 全部消息注册)
2. 编写 `messages.test.ts`(验证三种语言完整性)
3. 改造 `src/extension.ts`init + onDidChangeConfiguration
4. 改造 `src/activation/commands.ts`
5. 改造 `src/panel/webview.ts`
6. 改造 `src/views/setupView.ts`
7. 改造 `src/views/import-preview.ts`
8. 改造 `src/utils/report.ts`
9. 改造 `src/adapters/pmd.ts``sql-lint.ts`
10. 改造 `src/ai/engine.ts``openai-compatible.ts`
11. 改造各 converter 错误消息
12. 运行 `npm test` 验证
---
## 8. 边界处理
| 场景 | 处理 |
|------|------|
| 某个 key 不存在于 messages | fallback zh-CN,再 fallback 返回 key 本身 |
| 某条消息缺某种语言翻译 | fallback zh-CN |
| 传入非法语言值 | TypeScript 类型系统 `Language` 防止编译时错误 |
| setLanguage 同语言重复调 | 短路 return,不触发事件 |
| 切换语言时 webview 未打开 | 下次打开 webview 时取当前语言渲染,无影响 |
| 切换语言时正在显示通知 | 已显示的通知不更新,下次通知用新语言 |
---
## 9. 验证标准
- 在设置面板切语言后:设置面板文字立即变,审查报告文字立即变
- 重新触发审查:通知/进度条用新语言
- 导出报告:报告标题和标签用新语言
- 重启 VS Code:语言保持上次设置的值
- AI 审查:提示词不变,审查质量不退化