国际化翻译 Skill
name: dy-skill-i18n
by anyaoqi · published 2026-04-01
$ claw add gh:anyaoqi/anyaoqi-dy-skill-i18n---
name: dy-skill-i18n
description: 处理前端国际化翻译工作。当用户提到需要做国际化、i18n、翻译、多语言支持时使用此 skill。主要功能:1) 识别代码中的硬编码静态文字;2) 判断是否应使用现有公共翻译或需要新增;3) 将翻译添加到对应的语言文件中;4) 将硬编码替换为国际化调用;5) 检查翻译完整性和正确性。
---
# 国际化翻译 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: 识别需要翻译的内容
**✅ 需要翻译**:
**❌ 不需要翻译**:
Step 3: 判断翻译放哪里
根据项目实际结构判断翻译应该添加到哪里:
#### 情况A: 项目有模块区分
lang/
├── common/ # 公共翻译
│ ├── zh.ts
│ └── en.ts
├── module/ # 模块翻译
│ ├── product/
│ ├── order/
│ └── ...
└── index.ts**决策规则**:
#### 情况B: 项目无模块区分
lang/
├── zh_CN.ts
├── en_US.ts
└── index.ts**决策规则**:
#### 情况C: 其他结构
根据项目实际结构灵活处理:
Step 4: 添加翻译
1. **检查现有翻译** - 先搜索是否已有相同含义的翻译,有则复用
2. **新增翻译 key** - 如果没有,添加新的翻译
3. **确保所有支持的语言一致** - 添加时同步添加所有支持的语言,保持 key 路径一致
**添加位置判断**:
IF 项目有 common/ 或类似的公共目录 THEN
IF 翻译是通用性质的 THEN
添加到公共目录
ELSE
添加到对应模块目录
ELSE
添加到主语言文件中,使用模块前缀区分Step 5: 替换硬编码
根据项目实际的调用方式替换。常见使用方式如下:
<!-- 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: 项目没有模块目录怎么办
直接添加到主语言文件中,使用模块前缀区分:
// 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 的复数功能:
// zh_CN.ts(中文通常不需要复数变化,使用相同形式)
export default {
itemCount: "{n} 个项目 | {n} 个项目",
};
// en_US.ts
export default {
itemCount: "{n} item | {n} items",
};使用方式:
{{ $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. **使用项目配置** - 如果项目有全局的组件文案配置,优先使用
<!-- 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 中的静态文字:
<!-- 转换前 -->
<template #default="{ row }">
<span>产品名称:{{ row.name }}</span>
</template>
<!-- 转换后 -->
<template #default="{ row }">
<span>{{ $t("product.name") }}:{{ row.name }}</span>
</template>Q14: script 中如何使用翻译
<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`:
<!-- 不推荐:可能有 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 的组件:
// 使用 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 生命周期中强制刷新
<script setup>
import { watch } from "vue";
import { useI18n } from "vue-i18n";
const { locale } = useI18n();
// 监听语言变化,强制刷新
watch(locale, () => {
// 强制组件重新渲染
refreshData();
});
</script>2. **使用 key 变化** - 为 keep-alive 组件添加 key
<keep-alive>
<component :is="component" :key="locale" />
</keep-alive>Q18: 异步组件的翻译加载
异步组件的翻译需要在主应用或单独加载:
1. **如果项目使用全局翻译** - 异步组件会自动继承
2. **如果需要单独加载** - 使用 vue-i18n 的懒加载功能
// 懒加载翻译
const messages = {
en: () => import("./locales/en.json"),
zh: () => import("./locales/zh.json"),
};---
翻译准确性要求
作为经验丰富的翻译专家,你需要遵循以下严格要求:
1. 理解上下文语境
翻译前必须理解文字的使用场景:
| 场景 | 示例 | 翻译注意事项 |
| ---------- | ---------------------------- | -------------------- |
| 按钮文字 | "提交"、"确认"、"取消" | 简洁有力,通常用动词 |
| 表单标签 | "产品名称"、"创建时间" | 名词短语,明确指示 |
| 占位符 | "请输入名称"、"请选择日期" | 引导性语句 |
| 提示信息 | "操作成功"、"保存失败" | 结果导向,说明状态 |
| 对话框标题 | "新增产品"、"编辑品牌" | 动作 + 对象 |
| 表格列 | "状态"、"创建人"、"更新时间" | 简洁名词 |
2. 根据场景选择准确翻译
同一个中文词在不同场景可能有不同译法:
示例:中文 "状态"
├── 表格列标题 → "Status"
├── 业务流程中 → "State" 或 "Process Status"
├── 设备状态 → "Device Status"
└── 订单状态 → "Order Status"
示例:中文 "确认"
├── 按钮文字 → "Confirm" 或 "Submit"
├── 二次确认提示 → "Are you sure?"
└── 确认框标题 → "Confirmation"
示例:中文 "查看"
├── 表格操作列 → "View"
├── 查看详情 → "Details"
├── 查看记录 → "View Record"
└── 查看图片 → "Preview"3. 保持翻译一致性
✅ 正确:所有"产品名称"都翻译为 "Product Name"
❌ 错误:有时翻译为 "Product Name",有时翻译为 "Name"4. 专业术语处理
示例:
- SKU → Stock Keeping Unit(库存单位)
- MDM → Master Data Management(主数据管理)
- CRM → Customer Relationship Management(客户关系管理)5. key 命名规范
遵循项目的命名规范,项目没有特殊规定时参考以下原则:
命名原则:
- 使用小写字母
- 使用点分隔层级
- 保持简洁但有意义
- 避免缩写(除非是公认的缩写)
层级结构建议:
- 动作相关:{模块}.action.{具体动作}
product.action.add, product.action.edit, product.action.delete
- 字段相关:{模块}.{实体}.{字段名}
product.product.name, product.product.code
- 提示相关:{模块}.message.{类型}
product.message.success, product.message.error
- 占位符:common.placeholder.{类型}
common.placeholder.input, common.placeholder.select
公共部分:common.{类型}.{具体项}
common.action.add, common.action.edit
common.table.index, common.table.action
common.message.success, common.message.error6. 长度和空间考虑
7. 复数和单数
8. 大小写规范
9. 标点符号
---
翻译决策流程
当遇到翻译时,按以下流程决策:
1. 理解文字含义和用途
↓
2. 查看项目现有翻译库
├── 已有相同翻译?→ 直接复用
└── 没有?→ 继续
↓
3. 分析使用场景
├── 什么类型的UI元素?
├── 在什么上下文中使用?
└── 用户期望什么语气?
↓
4. 选择最准确的翻译
↓
5. 判断翻译放哪里
├── 通用性质?→ 公共目录
└── 模块特有?→ 模块目录
↓
6. 检查一致性
├── 是否与现有术语冲突?
└── 是否需要更新术语表?---
注意事项
1. **不修改项目结构** - 只在现有结构中添加内容,不创建新目录(除非项目本身就需要)
2. **保持风格一致** - 使用项目现有的命名风格(camelCase, kebab-case, SCREAMING_SNAKE_CASE 等)
3. **验证替换结果** - 修改后测试页面显示,确保没有破坏现有功能
4. **公共翻译不放模块** - 通用性质的翻译不要放到模块目录中
5. **模块翻译不放公共** - 特定业务模块的翻译不要放到公共目录中
6. **所有语言同步** - 项目支持的所有语言都要同步更新
7. **不破坏现有** - 所有修改基于现有规则,不改变现有结构
---
验证与错误处理
验证翻译质量
修改完成后,必须验证:
1. **语义准确性**:翻译是否准确表达原意?
2. **场景合适性**:在当前场景下是否自然?
3. **一致性检查**:类似文字是否使用类似翻译?
4. **拼写和语法**:英文翻译是否有拼写错误或语法问题?
5. **UI适配性**:翻译后是否超出UI空间?
验证方法
1. **运行时检查**:
- 切换语言测试页面显示
- 检查控制台是否有 missing translation 警告
2. **代码检查**:
- 搜索是否还有遗漏的硬编码
- 确认所有 key 都已正确添加
3. **如果项目有 lint 工具**:按项目规范执行
错误处理
1. **翻译错误**:
- 立即修正错误翻译
- 检查是否影响其他使用该翻译的地方
2. **遗漏翻译**:
- 补充遗漏的翻译
- 检查是否还有其他遗漏
3. **格式问题**:
- 保持与项目现有格式一致
- 使用项目的格式化工具(如果有)
4. **如果出现问题**:
- 使用 git 回滚到修改前
- 检查问题原因后重新修改
More tools from the same signal band
Order food/drinks (点餐) on an Android device paired as an OpenClaw node. Uses in-app menu and cart; add goods, view cart, submit order (demo, no real payment).
Sign plugins, rotate agent credentials without losing identity, and publicly attest to plugin behavior with verifiable claims and authenticated transfers.
The philosophical layer for AI agents. Maps behavior to Spinoza's 48 affects, calculates persistence scores, and generates geometric self-reports. Give your...