国际化 (i18n) 指南
了解如何为 MoeCopy AI 贡献翻译或添加新语言支持
概述
MoeCopy AI 使用基于 React Context 的国际化系统,支持运行时语言切换和跨扩展窗口的偏好同步。
当前支持的语言
- 简体中文 (
zh_CN) - 默认语言 - English (
en_US) - 英语
核心特性
- 🌐 自动检测 - 首次使用时自动检测浏览器语言
- 🔄 运行时切换 - 切换语言无需刷新页面,立即生效
- 💾 偏好同步 - 通过 Chrome sync storage 跨设备同步语言设置
- 🎯 参数插值 - 支持动态参数替换(如
t("key", { name: "value" })) - 📦 完整覆盖 - 452 个翻译键,涵盖所有用户可见文本
架构概览
utils/i18n/
├── context.tsx # React Context 和 Provider
├── types.ts # 类型定义和常量
├── translations.ts # 翻译加载逻辑
└── index.ts # 公共 API 导出
locales/
├── zh_CN.json # 简体中文翻译
└── en_US.json # 英语翻译
components/option/
└── LanguageSelect.tsx # 语言选择器组件翻译文件结构
文件位置
- 简体中文:
locales/zh_CN.json - 英语:
locales/en_US.json
键命名规范
翻译文件使用 扁平键结构,键名采用点号分隔的命名空间:
{
"common.ok": "确定",
"common.cancel": "取消",
"error.apiKeyNotSet": "未设置 API 密钥",
"batch.filter.preset.excludeImages.name": "排除图片链接"
}命名规范:
- 使用描述性名称 - 键名应清晰表达用途
- 分层组织 - 使用点号分隔命名空间(如
功能.子功能.具体项) - 保持一致性 - 同类功能使用相同的命名前缀
- 避免过长 - 平衡描述性和简洁性
常见命名空间:
common.*- 通用文本(按钮、操作)error.*- 错误消息setting.*- 设置相关batch.*- 批量抓取功能sidebar.*- 侧边栏相关ai.*- AI 功能相关
参数插值
支持在翻译中使用占位符:
{
"batch.progress.fetching": "正在抓取 {{current}}/{{total}}",
"error.fetchFailed": "抓取失败: {{error}}"
}使用时传入参数:
t("batch.progress.fetching", { current: 5, total: 10 })
// 输出: "正在抓取 5/10"为翻译贡献者
改进现有翻译
发现翻译不准确、不通顺或有更好的表达方式?
-
提交翻译问题 - 在 GitHub Issues 创建 issue
- 使用 "translation" 标签
- 说明问题:
- 翻译键名(如
batch.filter.title) - 当前翻译
- 建议的翻译
- 出现位置或上下文
- 翻译键名(如
示例:
标题: [Translation] 改进批量抓取过滤器标题翻译 翻译键: batch.filter.title 当前翻译: "过滤" 建议翻译: "链接过滤" 原因: 当前翻译过于简短,不清楚是过滤什么。建议改为"链接过滤"更明确。 出现位置: 侧边栏 > 批量抓取 > 过滤器面板标题 -
或直接提交 PR - 如果您熟悉 Git,可以直接修复翻译(见下方 PR 流程)
提交翻译 PR
前置要求
- Git 基础知识
- GitHub 账号
- 文本编辑器(推荐 VS Code)
步骤详解
1. Fork 项目
访问 moe-copy-ai,点击右上角 "Fork" 按钮。
2. 克隆到本地
git clone https://github.com/YOUR_USERNAME/moe-copy-ai.git
cd moe-copy-ai3. 创建翻译分支
# 从 dev 分支创建新分支
git checkout dev
git checkout -b fix/translation-zh-batch-filter分支命名建议:
fix/translation-zh-*- 修复中文翻译fix/translation-en-*- 修复英文翻译feat/translation-add-ja- 添加新语言(如日语)
4. 编辑翻译文件
打开翻译文件(使用支持 JSON 的编辑器):
- 中文翻译:
locales/zh_CN.json - 英文翻译:
locales/en_US.json
重要:
- ✅ DO: 只修改翻译的值(引号内的文本)
- ❌ DON'T: 不要修改键名、不要删除键、不要改变 JSON 结构
// ✅ 正确示例
{
"batch.filter.title": "链接过滤", // 只修改值
"batch.filter.desc": "使用正则表达式筛选链接"
}
// ❌ 错误示例
{
"batch.linkFilter.title": "链接过滤", // ❌ 不要修改键名
// "batch.filter.desc": "..." // ❌ 不要删除键
}5. 保持两个语言文件的键一致
修改翻译时,确保 zh_CN.json 和 en_US.json 拥有完全相同的键。
如果您只改进一种语言的翻译,另一种语言的对应键也应该存在(即使不修改其值)。
6. 格式化 JSON
使用 Prettier 或编辑器自动格式化:
# 如果安装了 pnpm
pnpm run format
# 或手动使用 Prettier
npx prettier --write locales/*.json7. 测试翻译(可选但推荐)
如果您会运行项目,建议本地测试:
pnpm install
pnpm dev在浏览器中加载扩展,切换语言查看效果。
8. 提交更改
git add locales/
git commit -m "fix: improve Chinese translation for batch filter"提交消息格式:
fix: improve Chinese translation for XXXfix: correct English translation for XXXfeat: add Japanese translation
9. 推送到您的 Fork
git push origin fix/translation-zh-batch-filter10. 创建 Pull Request
-
访问您的 Fork 页面(GitHub 会提示创建 PR)
-
点击 "Compare & pull request"
-
确保目标分支是
dev(不是main) -
填写 PR 描述:
## 翻译改进 ### 修改的翻译键 - `batch.filter.title`: "过滤" → "链接过滤" - `batch.filter.desc`: 改进描述文案 ### 修改原因 原翻译过于简短,不清楚是过滤什么。新翻译更明确且符合上下文。 ### 测试 - [x] 已本地测试两种语言 - [x] JSON 格式正确 - [x] 两个语言文件的键保持一致 -
提交 PR,等待审核
PR 注意事项
- ✅ 一次 PR 只改进相关的翻译(不要混入无关修改)
- ✅ 保持 JSON 格式整洁(使用 Prettier)
- ✅ 两个语言文件的键必须一致
- ✅ 提交消息和 PR 描述清晰
- ❌ 不要修改代码文件(只修改
locales/*.json) - ❌ 不要删除或重命名现有的键
添加新翻译键(针对开发者)
当开发新功能需要添加新的翻译时:
1. 在两个语言文件中添加相同的键
locales/zh_CN.json:
{
"newFeature.title": "新功能标题",
"newFeature.description": "功能描述"
}locales/en_US.json:
{
"newFeature.title": "New Feature Title",
"newFeature.description": "Feature description"
}2. 在组件中使用新键
function NewFeature() {
const { t } = useI18n()
return <h1>{t("newFeature.title")}</h1>
}3. 测试两种语言
在设置页面切换语言,确认新文本正确显示。
添加新语言
想为 MoeCopy AI 添加新语言支持(如日语、韩语、法语)?
步骤概览
- 创建翻译文件
- 更新类型定义
- 更新翻译加载逻辑
- 测试语言检测和切换
- 提交 PR
详细步骤
1. 创建新语言文件
复制现有语言文件作为模板:
# 例如添加日语
cp locales/zh_CN.json locales/ja_JP.json编辑 locales/ja_JP.json,将所有值翻译为目标语言。
语言代码格式: 使用 语言_地区 格式(如 ja_JP, ko_KR, fr_FR)
2. 更新类型定义
编辑 utils/i18n/types.ts:
// 添加新语言到 Locale 类型
export type Locale = "zh_CN" | "en_US" | "ja_JP"
// 更新支持的语言列表
export const SUPPORTED_LOCALES: Locale[] = ["zh_CN", "en_US", "ja_JP"]
// 添加语言显示名称
export const LOCALE_NAMES: Record<Locale, string> = {
zh_CN: "简体中文",
en_US: "English",
ja_JP: "日本語"
}
// 添加到浏览器语言映射
export const INTL_LOCALE_MAP: Record<string, Locale> = {
zh: "zh_CN",
"zh-CN": "zh_CN",
en: "en_US",
"en-US": "en_US",
ja: "ja_JP", // 添加日语映射
"ja-JP": "ja_JP"
}3. 更新翻译加载逻辑
编辑 utils/i18n/translations.ts:
export async function getTranslation(locale: Locale): Promise<Record<string, string>> {
switch (locale) {
case "zh_CN":
return (await import("../../locales/zh_CN.json")).default
case "en_US":
return (await import("../../locales/en_US.json")).default
case "ja_JP": // 添加新语言
return (await import("../../locales/ja_JP.json")).default
default:
return (await import("../../locales/zh_CN.json")).default
}
}4. 测试新语言
运行开发服务器:
pnpm dev测试项:
- 浏览器语言设置为新语言时,扩展能否自动检测
- 在设置页面能否看到新语言选项
- 切换到新语言后,所有界面文本是否正确显示
- 检查浏览器控制台,确认没有缺失键的警告
5. 提交 PR
按照上面的 "提交翻译 PR" 流程提交,PR 标题示例:
feat: add Japanese (ja_JP) translation support为开发者:使用翻译
在组件中使用 useI18n()
import { useI18n } from "~utils/i18n"
function MyComponent() {
const { t, locale } = useI18n()
return (
<div>
<h1>{t("myFeature.title")}</h1>
<p>{t("myFeature.description", { name: "MoeCopy" })}</p>
<p>Current language: {locale}</p>
</div>
)
}在非 React 代码中使用
对于无法使用 hooks 的场景(如 background scripts、工具函数),直接导入翻译:
import { getTranslation } from "~utils/i18n"
import { Storage } from "@plasmohq/storage"
async function showNotification() {
const storage = new Storage()
const locale = await storage.get("locale") || "zh_CN"
const translations = await getTranslation(locale)
chrome.notifications.create({
title: translations["notification.title"],
message: translations["notification.message"]
})
}常见用法示例
1. 简单文本
<button>{t("common.ok")}</button>2. 带参数的文本
<p>{t("batch.status.completed", { count: 10 })}</p>3. 在 props 中使用
<input
placeholder={t("search.placeholder")}
aria-label={t("search.ariaLabel")}
/>4. 在常量中使用翻译键
对于配置项,使用 labelKey 和 descKey 存储翻译键:
export const PRESETS = [
{
id: "excludeImages",
labelKey: "batch.filter.preset.excludeImages.name",
descKey: "batch.filter.preset.excludeImages.desc",
pattern: "\\.(jpg|jpeg|png|gif|svg|webp)$"
}
]
// 在组件中使用
<span>{t(preset.labelKey)}</span>开发检查清单
添加新功能时,确保:
- 所有用户可见文本都使用
t()函数 - 翻译键添加到 两个 语言文件(
zh_CN.json和en_US.json) - 键名使用描述性命名,符合命名规范
- 测试了两种语言的显示效果
- 如果使用参数插值,确保参数名清晰且一致
测试翻译
本地开发测试
1. 安装依赖
pnpm install2. 启动开发服务器
pnpm dev3. 加载扩展到浏览器
- 打开 Chrome,访问
chrome://extensions/ - 启用"开发者模式"(右上角)
- 点击"加载已解压的扩展程序"
- 选择项目中的
build/chrome-mv3-dev/目录
4. 测试语言切换
- 点击扩展图标,打开弹窗
- 进入设置页面
- 找到"语言设置"部分
- 切换语言,观察界面变化
5. 测试自动检测
- 在
chrome://extensions/移除扩展 - 修改浏览器语言设置:
- Chrome: 设置 > 语言 > 首选语言
- 重新加载扩展
- 打开扩展,确认语言已自动设置
检查常见问题
缺失翻译键
打开浏览器控制台(F12),查找警告信息:
[i18n] Translation key not found: some.missing.key解决方法: 在两个语言文件中添加缺失的键。
参数插值未生效
检查翻译文件中的占位符格式是否正确:
// ✅ 正确
"message": "Hello {{name}}"
// ❌ 错误
"message": "Hello {name}"
"message": "Hello ${name}"语言切换后部分文本未更新
可能原因:
- 组件未使用
useI18n()hook - 硬编码文本未迁移到翻译系统
解决方法: 搜索代码中的硬编码字符串,替换为 t() 调用。
常见问题 (FAQ)
Q: 我不会编程,只想改进翻译,怎么办?
A: 您可以直接在 GitHub Issues 提交翻译建议,使用 "translation" 标签,说明当前翻译和建议翻译即可。
Q: 提交翻译 PR 需要测试吗?
A: 不是必须的,但强烈推荐。如果您不方便本地测试,请在 PR 中说明,维护者会帮忙测试。
Q: 可以一次提交多个翻译改进吗?
A: 可以,但建议相关的翻译改进放在一起。例如,"改进批量抓取相关的所有翻译"比"改进10个不相关的翻译"更好。
Q: 翻译文件很大,如何快速找到要改的键?
A: 使用编辑器的搜索功能(Ctrl+F / Cmd+F)搜索关键词。或者在界面上找到要改的文本,然后在代码中搜索该文本,找到对应的翻译键。
Q: 我添加的新语言什么时候会发布?
A: 新语言支持会在下一个版本发布时合并。通常在 PR 合并后的几周内发布。
Q: 如何确保两个语言文件的键一致?
A: 可以使用工具检查:
# 检查键是否一致(需要安装 jq)
diff <(jq -r 'keys[]' locales/zh_CN.json | sort) \
<(jq -r 'keys[]' locales/en_US.json | sort)如果有差异,输出会显示缺失或多余的键。
资源链接
- GitHub 仓库: https://github.com/yusixian/moe-copy-ai
- 提交 Issue: https://github.com/yusixian/moe-copy-ai/issues/new
- 贡献指南: CONTRIBUTING.md
- 翻译文件位置:
联系方式
有问题或建议?
- 在 GitHub Discussions 发起讨论
- 提交 GitHub Issue
- 邮箱: i@cosine.ren
Last updated on