15 KiB
15 KiB
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 类型定义
export type Language = 'zh-CN' | 'en' | 'ja';
const defaultLang: Language = 'zh-CN';
2.2 消息注册
单文件注册所有文案,每条包含三种语言的值。命名采用 模块.场景.含义,点分隔,全小写。
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() 函数
export function t(key: string): string {
const msg = messages[key]?.[currentLang] ?? messages[key]?.[defaultLang];
return msg ?? key;
}
插件内所有文案都通过 t() 获取,不再硬编码。无模板插值参数,简单直接。
2.5 语言切换
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() 中:
// 从当前配置初始化语言
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:
// 只推送当前 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 端处理
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. 实现顺序
- 创建
src/i18n/messages.ts(核心模块 + 全部消息注册) - 编写
messages.test.ts(验证三种语言完整性) - 改造
src/extension.ts(init + onDidChangeConfiguration) - 改造
src/activation/commands.ts - 改造
src/panel/webview.ts - 改造
src/views/setupView.ts - 改造
src/views/import-preview.ts - 改造
src/utils/report.ts - 改造
src/adapters/pmd.ts、sql-lint.ts - 改造
src/ai/engine.ts、openai-compatible.ts - 改造各 converter 错误消息
- 运行
npm test验证
8. 边界处理
| 场景 | 处理 |
|---|---|
| 某个 key 不存在于 messages | fallback zh-CN,再 fallback 返回 key 本身 |
| 某条消息缺某种语言翻译 | fallback zh-CN |
| 传入非法语言值 | TypeScript 类型系统 Language 防止编译时错误 |
| setLanguage 同语言重复调 | 短路 return,不触发事件 |
| 切换语言时 webview 未打开 | 下次打开 webview 时取当前语言渲染,无影响 |
| 切换语言时正在显示通知 | 已显示的通知不更新,下次通知用新语言 |
9. 验证标准
- 在设置面板切语言后:设置面板文字立即变,审查报告文字立即变
- 重新触发审查:通知/进度条用新语言
- 导出报告:报告标题和标签用新语言
- 重启 VS Code:语言保持上次设置的值
- AI 审查:提示词不变,审查质量不退化