Install
openclaw skills install dy-skill-i18n处理前端国际化翻译工作。当用户提到需要做国际化、i18n、翻译、多语言支持时使用此 skill。主要功能:1) 识别代码中的硬编码静态文字;2) 判断是否应使用现有公共翻译或需要新增;3) 将翻译添加到对应的语言文件中;4) 将硬编码替换为国际化调用;5) 检查翻译完整性和正确性。
openclaw skills install dy-skill-i18n此 skill 用于处理前端项目的国际化翻译工作。当你需要将代码中的硬编码中文(或英文)文字转换为国际化支持时使用。
前提:以实际项目结构为准,不破坏现有项目规则。
首先探索项目的国际化配置:
✅ 需要翻译:
❌ 不需要翻译:
根据项目实际结构判断翻译应该添加到哪里:
lang/
├── common/ # 公共翻译
│ ├── zh.ts
│ └── en.ts
├── module/ # 模块翻译
│ ├── product/
│ ├── order/
│ └── ...
└── index.ts
决策规则:
common/lang/
├── zh_CN.ts
├── en_US.ts
└── index.ts
决策规则:
product.*, order.*, common.*根据项目实际结构灵活处理:
添加位置判断:
IF 项目有 common/ 或类似的公共目录 THEN
IF 翻译是通用性质的 THEN
添加到公共目录
ELSE
添加到对应模块目录
ELSE
添加到主语言文件中,使用模块前缀区分
根据项目实际的调用方式替换。常见使用方式如下:
<!-- Template 中使用 $t -->
{{ $t("key.path") }}
<!-- 在 script 中使用 -->
<script setup>
const { t } = useI18n();
const text = t("key.path");
</script>
<!-- 属性绑定 -->
<el-input :placeholder="t('key.path')" />
完整性检查:
正确性检查:
遗漏检查:
通用性质 = 任何模块都可能用到
- 操作类:新增、编辑、删除、查看、导入、导出、提交、审核
- 提示类:确定、取消、关闭,保存、成功、失败
- 占位符类:请输入、请选择、开始日期、结束日期
- 表格类:序号、操作、状态
模块特有 = 只有特定模块使用
- 产品名称、产品编码、产品分类
- 订单号、订单状态
- 客户名称、客户电话
直接添加到主语言文件中,使用模块前缀区分:
// zh_CN.ts
export default {
common: {
action: { add: "新增", edit: "编辑" },
},
product: {
name: "产品名称",
code: "产品编码",
},
order: {
no: "订单号",
status: "订单状态",
},
};
如果业务确实需要独立管理(如不同模块由不同人维护),可以在模块目录中重复定义,但建议优先复用公共翻译。
如果翻译中需要动态参数(如用户名、数量、时间等):
在 key 中使用占位符:
// zh_CN.ts
export default {
deleteSuccess: "删除成功,共 {count} 条记录",
welcome: "欢迎 {username} 登录系统",
selectedCount: "已选择 {n} 项",
};
在组件中传递参数:
<!-- 使用对象参数 -->
{{ $t("deleteSuccess", { count: deletedCount }) }}
<!-- 在 script 中 -->
<script setup>
const { t } = useI18n();
const message = t("deleteSuccess", { count: deletedCount });
</script>
注意事项:
{count} 或 %{count})如果项目使用 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。
日期、数字、货币通常需要专门的格式化方法,不要使用简单的翻译:
使用项目现有的格式化方法:
dayjs, Intl.NumberFormat 等如果确实需要翻译日期格式:
// 仅翻译格式说明文字,不翻译实际日期
dateFormat: {
year: '年',
month: '月',
day: '日'
}
数字和货币:
// 建议使用专门的格式化,不走翻译
// 如:Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' })
对于 Element Plus、Ant Design 等 UI 框架的组件:
empty-text, confirm-btn-text 等)<!-- Element Plus 示例 -->
<el-table :data="list" :empty-text="$t('common.table.empty')">
</el-table>
如果后端返回的是国际化 key - 直接使用,无需翻译
如果后端返回原始错误信息 - 建议:
如果是业务性质的提示 - 可以翻译:
// 错误码映射
error: {
1001: '用户名不存在',
1002: '密码错误',
1003: '账号已被锁定'
}
项目中可能存在部分已国际化但风格不一致的代码:
对于 scoped slot 中的静态文字:
<!-- 转换前 -->
<template #default="{ row }">
<span>产品名称:{{ row.name }}</span>
</template>
<!-- 转换后 -->
<template #default="{ row }">
<span>{{ $t("product.name") }}:{{ row.name }}</span>
</template>
<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>
谨慎使用 v-html:
<!-- 不推荐:可能有 XSS 风险 -->
<span v-html="$t('message.withHtml')"></span>
<!-- 推荐:分开处理 -->
<span>{{ $t('message.prefix') }}<a :href="url">{{ $t('message.link') }}</a></span>
如果必须使用 v-html,确保内容来自可信来源。
对于使用 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"));
使用 keep-alive 缓存的组件在切换语言后可能不会重新渲染:
<script setup>
import { watch } from "vue";
import { useI18n } from "vue-i18n";
const { locale } = useI18n();
// 监听语言变化,强制刷新
watch(locale, () => {
// 强制组件重新渲染
refreshData();
});
</script>
<keep-alive>
<component :is="component" :key="locale" />
</keep-alive>
异步组件的翻译需要在主应用或单独加载:
// 懒加载翻译
const messages = {
en: () => import("./locales/en.json"),
zh: () => import("./locales/zh.json"),
};
作为经验丰富的翻译专家,你需要遵循以下严格要求:
翻译前必须理解文字的使用场景:
| 场景 | 示例 | 翻译注意事项 |
|---|---|---|
| 按钮文字 | "提交"、"确认"、"取消" | 简洁有力,通常用动词 |
| 表单标签 | "产品名称"、"创建时间" | 名词短语,明确指示 |
| 占位符 | "请输入名称"、"请选择日期" | 引导性语句 |
| 提示信息 | "操作成功"、"保存失败" | 结果导向,说明状态 |
| 对话框标题 | "新增产品"、"编辑品牌" | 动作 + 对象 |
| 表格列 | "状态"、"创建人"、"更新时间" | 简洁名词 |
同一个中文词在不同场景可能有不同译法:
示例:中文 "状态"
├── 表格列标题 → "Status"
├── 业务流程中 → "State" 或 "Process Status"
├── 设备状态 → "Device Status"
└── 订单状态 → "Order Status"
示例:中文 "确认"
├── 按钮文字 → "Confirm" 或 "Submit"
├── 二次确认提示 → "Are you sure?"
└── 确认框标题 → "Confirmation"
示例:中文 "查看"
├── 表格操作列 → "View"
├── 查看详情 → "Details"
├── 查看记录 → "View Record"
└── 查看图片 → "Preview"
✅ 正确:所有"产品名称"都翻译为 "Product Name"
❌ 错误:有时翻译为 "Product Name",有时翻译为 "Name"
示例:
- SKU → Stock Keeping Unit(库存单位)
- MDM → Master Data Management(主数据管理)
- CRM → Customer Relationship Management(客户关系管理)
遵循项目的命名规范,项目没有特殊规定时参考以下原则:
命名原则:
- 使用小写字母
- 使用点分隔层级
- 保持简洁但有意义
- 避免缩写(除非是公认的缩写)
层级结构建议:
- 动作相关:{模块}.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.error
当遇到翻译时,按以下流程决策:
1. 理解文字含义和用途
↓
2. 查看项目现有翻译库
├── 已有相同翻译?→ 直接复用
└── 没有?→ 继续
↓
3. 分析使用场景
├── 什么类型的UI元素?
├── 在什么上下文中使用?
└── 用户期望什么语气?
↓
4. 选择最准确的翻译
↓
5. 判断翻译放哪里
├── 通用性质?→ 公共目录
└── 模块特有?→ 模块目录
↓
6. 检查一致性
├── 是否与现有术语冲突?
└── 是否需要更新术语表?
修改完成后,必须验证:
运行时检查:
代码检查:
如果项目有 lint 工具:按项目规范执行
翻译错误:
遗漏翻译:
格式问题:
如果出现问题: