MoeCopy AI LogoMoeCopy AI
开发者文档

国际化 (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": "排除图片链接"
}

命名规范:

  1. 使用描述性名称 - 键名应清晰表达用途
  2. 分层组织 - 使用点号分隔命名空间(如 功能.子功能.具体项
  3. 保持一致性 - 同类功能使用相同的命名前缀
  4. 避免过长 - 平衡描述性和简洁性

常见命名空间:

  • 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"

为翻译贡献者

改进现有翻译

发现翻译不准确、不通顺或有更好的表达方式?

  1. 提交翻译问题 - 在 GitHub Issues 创建 issue

    • 使用 "translation" 标签
    • 说明问题:
      • 翻译键名(如 batch.filter.title
      • 当前翻译
      • 建议的翻译
      • 出现位置或上下文

    示例:

    标题: [Translation] 改进批量抓取过滤器标题翻译
    
    翻译键: batch.filter.title
    当前翻译: "过滤"
    建议翻译: "链接过滤"
    
    原因: 当前翻译过于简短,不清楚是过滤什么。建议改为"链接过滤"更明确。
    出现位置: 侧边栏 > 批量抓取 > 过滤器面板标题
  2. 或直接提交 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-ai

3. 创建翻译分支

# 从 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.jsonen_US.json 拥有完全相同的键

如果您只改进一种语言的翻译,另一种语言的对应键也应该存在(即使不修改其值)。

6. 格式化 JSON

使用 Prettier 或编辑器自动格式化:

# 如果安装了 pnpm
pnpm run format

# 或手动使用 Prettier
npx prettier --write locales/*.json

7. 测试翻译(可选但推荐)

如果您会运行项目,建议本地测试:

pnpm install
pnpm dev

在浏览器中加载扩展,切换语言查看效果。

8. 提交更改

git add locales/
git commit -m "fix: improve Chinese translation for batch filter"

提交消息格式:

  • fix: improve Chinese translation for XXX
  • fix: correct English translation for XXX
  • feat: add Japanese translation

9. 推送到您的 Fork

git push origin fix/translation-zh-batch-filter

10. 创建 Pull Request

  1. 访问您的 Fork 页面(GitHub 会提示创建 PR)

  2. 点击 "Compare & pull request"

  3. 确保目标分支是 dev(不是 main

  4. 填写 PR 描述:

    ## 翻译改进
    
    ### 修改的翻译键
    - `batch.filter.title`: "过滤" → "链接过滤"
    - `batch.filter.desc`: 改进描述文案
    
    ### 修改原因
    原翻译过于简短,不清楚是过滤什么。新翻译更明确且符合上下文。
    
    ### 测试
    - [x] 已本地测试两种语言
    - [x] JSON 格式正确
    - [x] 两个语言文件的键保持一致
  5. 提交 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 添加新语言支持(如日语、韩语、法语)?

步骤概览

  1. 创建翻译文件
  2. 更新类型定义
  3. 更新翻译加载逻辑
  4. 测试语言检测和切换
  5. 提交 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. 在常量中使用翻译键

对于配置项,使用 labelKeydescKey 存储翻译键:

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.jsonen_US.json
  • 键名使用描述性命名,符合命名规范
  • 测试了两种语言的显示效果
  • 如果使用参数插值,确保参数名清晰且一致

测试翻译

本地开发测试

1. 安装依赖

pnpm install

2. 启动开发服务器

pnpm dev

3. 加载扩展到浏览器

  1. 打开 Chrome,访问 chrome://extensions/
  2. 启用"开发者模式"(右上角)
  3. 点击"加载已解压的扩展程序"
  4. 选择项目中的 build/chrome-mv3-dev/ 目录

4. 测试语言切换

  1. 点击扩展图标,打开弹窗
  2. 进入设置页面
  3. 找到"语言设置"部分
  4. 切换语言,观察界面变化

5. 测试自动检测

  1. chrome://extensions/ 移除扩展
  2. 修改浏览器语言设置:
    • Chrome: 设置 > 语言 > 首选语言
  3. 重新加载扩展
  4. 打开扩展,确认语言已自动设置

检查常见问题

缺失翻译键

打开浏览器控制台(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)

如果有差异,输出会显示缺失或多余的键。

资源链接

联系方式

有问题或建议?

Edit on GitHub

Last updated on

On this page