dy-skill-i18n

GitHub 作者 LeoYeAI/openclaw-master-skills

处理前端国际化翻译工作。当用户提到需要做国际化、i18n、翻译、多语言支持时使用此 skill。主要功能:1) 识别代码中的硬编码静态文字;2) 判断是否应使用现有公共翻译或需要新增;3) 将翻译添加到对应的语言文件中;4) 将硬编码替换为国际化调用;5) 检查翻译完整性和正确性。

安装 / 下载方式

TotalClaw CLI推荐
totalclaw skills install shop:github:LeoYeAI~openclaw-master-skills~dy-skill-i18n
cURL直接下载,无需登录
curl -fsSL https://skills.taituai.com/api/skills/github%3ALeoYeAI~openclaw-master-skills~dy-skill-i18n/file -o dy-skill-i18n.md
# 国际化翻译 Skill

## 目录

- [核心原则](#核心原则)
- [工作流程](#工作流程)
- [常见问题处理](#常见问题处理)
- [翻译准确性要求](#翻译准确性要求)
- [注意事项](#注意事项)
- [验证与错误处理](#验证与错误处理)

---

## 概述

此 skill 用于处理前端项目的国际化翻译工作。当你需要将代码中的硬编码中文(或英文)文字转换为国际化支持时使用。

**前提**:以实际项目结构为准,不破坏现有项目规则。

---

## 核心原则

1. **以实际项目结构为准** - 不假设固定目录结构,根据项目现有配置灵活调整
2. **配置放对应位置** - 公共翻译放公共目录,模块翻译放模块目录,不混淆
3. **优先复用现有翻译** - 已有相同含义的翻译时直接使用,不要重复添加
4. **只翻译静态文字** - 不翻译后端接口返回的数据、字典/枚举中的内容
5. **不破坏现有规则** - 所有修改基于项目现有规则,不改变现有结构

---

## 工作流程

### Step 1: 了解项目结构

首先探索项目的国际化配置:

1. 查找语言文件目录(常见名称:lang, locales, i18n, language, messages)
2. 确认目录结构模式:
   - **有模块区分**:是否存在 common/, module/, 或按模块名划分的子目录
   - **无模块区分**:所有翻译是否都在一个或少数几个文件中
3. 确认文件命名规范(如 zh_CN.ts, zh-CN.ts, en.ts, en_US.ts 等)
4. 确认国际化调用方式(如 $t(), t(), useI18n() 等)
5. 确认支持的语言种类(是否只有中英文,还是支持更多语言)
6. 确认是否使用了 vue-i18n 的特殊功能(复数、插值等)

### Step 2: 识别需要翻译的内容

**✅ 需要翻译**:

- 静态显示的中文/英文字符串
- 按钮文字、提示信息、表单标签
- 表格列标题、对话框标题
- 占位符文字、选项文字

**❌ 不需要翻译**:

- 从后端 API 返回的数据
- 字典/枚举中获取的内容
- 变量和动态计算的值
- 数字、日期格式(需要使用专门的格式化方法)
- 技术性错误信息(后端返回的 error message)

### Step 3: 判断翻译放哪里

根据项目实际结构判断翻译应该添加到哪里:

#### 情况A: 项目有模块区分

```
lang/
├── common/        # 公共翻译
│   ├── zh.ts
│   └── en.ts
├── module/        # 模块翻译
│   ├── product/
│   ├── order/
│   └── ...
└── index.ts
```

**决策规则**:

- 如果是**通用性质**的翻译(如:新增、编辑、删除、搜索、重置、确定、取消、请输入、请选择等)→ 放 `common/`
- 如果是**特定业务模块**的翻译(如:产品名称、订单状态,品牌分类等)→ 放对应模块目录

#### 情况B: 项目无模块区分

```
lang/
├── zh_CN.ts
├── en_US.ts
└── index.ts
```

**决策规则**:

- 所有翻译都直接添加到对应的语言文件中
- 使用有前缀的 key 区分不同模块,如 `product.*`, `order.*`, `common.*`

#### 情况C: 其他结构

根据项目实际结构灵活处理:

- 可能存在多个语言文件(按语言分、按模块分、混合分)
- 可能使用 JSON 格式或 TS 格式
- 总之**遵循项目现有模式,不打破现有结构**

### Step 4: 添加翻译

1. **检查现有翻译** - 先搜索是否已有相同含义的翻译,有则复用
2. **新增翻译 key** - 如果没有,添加新的翻译
3. **确保所有支持的语言一致** - 添加时同步添加所有支持的语言,保持 key 路径一致

**添加位置判断**:

```
IF 项目有 common/ 或类似的公共目录 THEN
  IF 翻译是通用性质的 THEN
    添加到公共目录
  ELSE
    添加到对应模块目录
ELSE
  添加到主语言文件中,使用模块前缀区分
```

### Step 5: 替换硬编码

根据项目实际的调用方式替换。常见使用方式如下:

```vue
<!-- Template 中使用 $t -->
{{ $t("key.path") }}

<!-- 在 script 中使用 -->
<script setup>
const { t } = useI18n();
const text = t("key.path");
</script>

<!-- 属性绑定 -->
<el-input :placeholder="t('key.path')" />
```

### Step 6: 检查验证

1. **完整性检查**:
   - 所有支持的语言翻译都已添加
   - key 路径在所有语言文件中一致

2. **正确性检查**:
   - 翻译 key 正确
   - 页面显示正常

3. **遗漏检查**:
   - 扫描文件确认没有遗漏的硬编码
   - 检查相似场景是否需要同样处理

---

## 常见问题处理

### Q1: 如何判断是公共翻译还是模块翻译?

```
通用性质 = 任何模块都可能用到
- 操作类:新增、编辑、删除、查看、导入、导出、提交、审核
- 提示类:确定、取消、关闭,保存、成功、失败
- 占位符类:请输入、请选择、开始日期、结束日期
- 表格类:序号、操作、状态

模块特有 = 只有特定模块使用
- 产品名称、产品编码、产品分类
- 订单号、订单状态
- 客户名称、客户电话
```

### Q2: 项目没有模块目录怎么办

直接添加到主语言文件中,使用模块前缀区分:

```typescript
// zh_CN.ts
export default {
  common: {
    action: { add: "新增", edit: "编辑" },
  },
  product: {
    name: "产品名称",
    code: "产品编码",
  },
  order: {
    no: "订单号",
    status: "订单状态",
  },
};
```

### Q3: 已有公共翻译但想保持模块独立性

如果业务确实需要独立管理(如不同模块由不同人维护),可以在模块目录中重复定义,但建议优先复用公共翻译。

### Q4: 如何处理动态参数/插值

如果翻译中需要动态参数(如用户名、数量、时间等):

1. **在 key 中使用占位符**:

   ```typescript
   // zh_CN.ts
   export default {
     deleteSuccess: "删除成功,共 {count} 条记录",
     welcome: "欢迎 {username} 登录系统",
     selectedCount: "已选择 {n} 项",
   };
   ```

2. **在组件中传递参数**:

   ```vue
   <!-- 使用对象参数 -->
   {{ $t("deleteSuccess", { count: deletedCount }) }}

   <!-- 在 script 中 -->
   <script setup>
   const { t } = useI18n();
   const message = t("deleteSuccess", { count: deletedCount });
   </script>
   ```

3. **注意事项**:
   - 遵循项目现有的插值语法(占位符格式可能不同,如 `{count}` 或 `%{count}`)
   - 占位符命名应简洁明了
   - 确保所有语言中占位符名称一致

### Q5: 如何处理复数形式

如果项目使用 vue-i18n 的复数功能:

```typescript
// zh_CN.ts(中文通常不需要复数变化,使用相同形式)
export default {
  itemCount: "{n} 个项目 | {n} 个项目",
};

// en_US.ts
export default {
  itemCount: "{n} item | {n} items",
};
```

使用方式:

```vue
{{ $t("itemCount", { n: count }, count) }}
```

vue-i18n 会根据 count 的值自动选择单数或复数形式。

**注意**:如果项目没有使用复数功能,将复数和单数分别定义为不同的 key,如 `itemCount` 和 `itemCount_plural`。

### Q6: 如何处理日期/数字/货币格式化

日期、数字、货币通常需要专门的格式化方法,**不要**使用简单的翻译:

1. **使用项目现有的格式化方法**:
   - 查找项目中是否有现成的格式化工具
   - 如使用 `dayjs`, `Intl.NumberFormat` 等

2. **如果确实需要翻译日期格式**:

   ```typescript
   // 仅翻译格式说明文字,不翻译实际日期
   dateFormat: {
     year: '年',
     month: '月',
     day: '日'
   }
   ```

3. **数字和货币**:
   ```typescript
   // 建议使用专门的格式化,不走翻译
   // 如:Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' })
   ```

### Q7: 如何处理 UI 框架组件的国际化

对于 Element Plus、Ant Design 等 UI 框架的组件:

1. **遵循项目配置** - 查看项目是否已经全局配置了框架的 i18n
2. **组件自带属性** - 很多组件有内置的 i18n 属性(如 `empty-text`, `confirm-btn-text` 等)
3. **使用项目配置** - 如果项目有全局的组件文案配置,优先使用

```vue
<!-- Element Plus 示例 -->
<el-table :data="list" :empty-text="$t('common.table.empty')">
</el-table>
```

### Q8: 如何处理后端返回的错误信息

1. **如果后端返回的是国际化 key** - 直接使用,无需翻译
2. **如果后端返回原始错误信息** - 建议:
   - 在前端建立错误码映射表
   - 或让后端支持国际化 key 返回

3. **如果是业务性质的提示** - 可以翻译:
   ```typescript
   // 错误码映射
   error: {
     1001: '用户名不存在',
     1002: '密码错误',
     1003: '账号已被锁定'
   }
   ```

### Q9: 项目需要支持多种语言怎么办

1. **确认支持的语言种类** - 查看项目现有语言文件
2. **所有语言同步添加** - 新增翻译时,必须同时添加所有支持的语言
3. **遵循语言代码规范** - 使用项目现有的语言代码格式(如 zh-CN vs zh_CN)

### Q10: 翻译 key 冲突怎么办

1. **检查现有 key** - 确认是否真的冲突
2. **使用更具体的路径** - 添加模块前缀区分
3. **与项目成员协商** - 如果是公共 key 的修改,需沟通

### Q11: 如何处理部分国际化的遗留代码

项目中可能存在部分已国际化但风格不一致的代码:

1. **保持现有风格** - 不强制统一,避免大规模改动
2. **新添加的遵循规范** - 新翻译使用规范写法
3. **必要时可重构** - 如果发现明显问题,可提出建议

### Q12: 旧翻译 key 废弃怎么办

1. **保留旧翻译** - 暂时保留,避免线上报错
2. **标记废弃** - 添加注释说明已废弃
3. **后续清理** - 定期清理未使用的废弃翻译

### Q13: 如何处理 slot 中的文字

对于 scoped slot 中的静态文字:

```vue
<!-- 转换前 -->
<template #default="{ row }">
  <span>产品名称:{{ row.name }}</span>
</template>

<!-- 转换后 -->
<template #default="{ row }">
  <span>{{ $t("product.name") }}:{{ row.name }}</span>
</template>
```

### Q14: script 中如何使用翻译

```vue
<script setup>
// useI18n 通常由项目自动导入,无需手动 import
// 如项目无自动导入,则需要:import { useI18n } from 'vue-i18n'

const { t } = useI18n();

// 在方法中使用
function handleClick() {
  const message = t("common.message.success");
  ElMessage.success(message);
}

// 在计算属性中使用
const statusText = computed(() => {
  return t(`product.status.${status.value}`);
});
</script>
```

### Q15: v-html 和 $t 结合使用需要注意什么

**谨慎使用** `v-html`:

```vue
<!-- 不推荐:可能有 XSS 风险 -->
<span v-html="$t('message.withHtml')"></span>

<!-- 推荐:分开处理 -->
<span>{{ $t('message.prefix') }}<a :href="url">{{ $t('message.link') }}</a></span>
```

如果必须使用 `v-html`,确保内容来自可信来源。

### Q16: 函数式组件中如何使用翻译

对于使用 render 函数或 JSX 的组件:

```typescript
// 使用 this.$t(需要确保组件可访问到 i18n 实例)
export default {
  render() {
    return h("span", this.$t("key.path"));
  },
};

// 或者通过 provide/inject 传递翻译函数
// 父组件
const { t } = useI18n();
provide("i18n", { t });

// 子组件
const { t } = inject("i18n");
return h("span", t("key.path"));
```

### Q17: keep-alive 缓存组件翻译切换问题

使用 keep-alive 缓存的组件在切换语言后可能不会重新渲染:

1. **监听语言变化** - 在 activated 生命周期中强制刷新

```vue
<script setup>
import { watch } from "vue";
import { useI18n } from "vue-i18n";

const { locale } = useI18n();

// 监听语言变化,强制刷新
watch(locale, () => {
  // 强制组件重新渲染
  refreshData();
});
</script>
```

2. **使用 key 变化** - 为 keep-alive 组件添加 key

```vue
<keep-alive>
  <component :is="component" :key="locale" />
</keep-alive>
```

### Q18: 异步组件的翻译加载

异步组件的翻译需要在主应用或单独加载:

1. **如果项目使用全局翻译** - 异步组件会自动继承
2. **如果需要单独加载** - 使用 vue-i18n 的懒加载功能

```typescript
// 懒加载翻译
const messages = {
  en: () => import("./locales/en.json"),
  zh: () => import("./locales/zh.json"),
};
```

---

## 翻译准确性要求

作为经验丰富的翻译专家,你需要遵循以下严格要求:

### 1. 理解上下文语境

翻译前必须理解文字的使用场景:

| 场景       | 示例                         | 翻译注意事项         |
| ---------- | ---------------------------- | -------------------- |
| 按钮文字   | "提交"、"确认"、"取消"       | 简洁有力,通常用动词 |
| 表单标签   | "产品名称"、"创建时间"       | 名词短语,明确指示   |
| 占位符     | "请输入名称"、"请选择日期"   | 引导性语句           |
| 提示信息   | "操作成功"、"保存失败"       | 结果导向,说明状态   |
| 对话框标题 | "新增产品"、"编辑品牌"       | 动作 + 对象          |
| 表格列     | "状态"、"创建人"、"更新时间" | 简洁名词             |

### 2. 根据场景选择准确翻译

同一个中文词在不同场景可能有不同译法:

```
示例:中文 "状态"
├── 表格列标题 → "Status"
├── 业务流程中 → "State" 或 "Process Status"
├── 设备状态 → "Device Status"
└── 订单状态 → "Order Status"

示例:中文 "确认"
├── 按钮文字