7.5 KiB
7.5 KiB
name, version, description
| name | version | description |
|---|---|---|
| i18n | 3.0.0 | 使用 vue-i18n 管理 Vue 3 应用的国际化。当需要添加多语言支持或管理翻译时使用。含语言包管理和路由国际化。 |
⚠️ 核心执行流程已在
.cursor/rules/skill-i18n.mdc中由 Cursor 自动注入。 本文件提供完整模板、代码示例和边缘场景处理,供 Agent 按需深入 Read。
Internationalization (i18n) — Vue 3 + vue-i18n
触发条件
用户要求添加多语言支持、翻译内容、配置国际化路由。
执行流程
1. 检测现有 i18n 方案
# 检查是否已有 i18n 库
grep -E "vue-i18n|@intlify" package.json 2>/dev/null
ls src/i18n* src/locales* 2>/dev/null
2. 初始化 i18n(如未配置)
推荐方案:vue-i18n(Vue 3 官方国际化方案)
npm install vue-i18n@9
创建目录结构:
src/locales/
├── index.ts # i18n 实例配置
├── zh-CN.ts # 中文(简体)— 使用 JS 扁平对象格式
├── en.ts # 英文 — 使用 JS 扁平对象格式
└── modules/ # 按模块拆分(大型项目,同样使用 JS 格式)
├── common.zh-CN.ts
└── common.en.ts
格式选择:统一使用
.ts文件(export default { 'key': 'value' }), 而非.json。原因:JS 格式支持注释、支持函数(复数规则)、支持扁平键名中的特殊字符, 且与 vue-i18n v9 的 Composition API 配合更灵活。 若项目已使用.json,保持现有格式,不强制迁移。
3. i18n 配置
// src/locales/index.ts
import { createI18n } from 'vue-i18n'
import zhCN from './zh-CN.ts'
import en from './en.ts'
const i18n = createI18n({
legacy: false,
locale: localStorage.getItem('locale') || 'zh-CN',
fallbackLocale: 'en',
messages: {
'zh-CN': zhCN,
en,
},
})
export default i18n
// src/main.ts
import i18n from './locales'
app.use(i18n)
4. 翻译键命名规范
使用扁平 dot notation(不使用嵌套对象),避免深层路径冲突:
// ✅ 扁平 dot notation — 清晰、无歧义、工具友好
export default {
'common.action.save': '保存',
'common.action.cancel': '取消',
'common.action.delete': '删除',
'common.status.loading': '加载中...',
'auth.action.login': '登录',
'auth.action.logout': '退出登录',
'auth.action.signUp': '注册',
'home.hero.title': '欢迎',
'home.hero.description': '...',
}
// ❌ 嵌套对象 — 容易产生路径冲突
export default {
common: { save: '保存' },
pages: { home: { title: '...' } },
}
命名公式:{feature}.{context}.{action|status|label}
| 段 | 含义 | 示例 |
|---|---|---|
feature |
业务功能/模块 | order、user、common |
context |
所在 UI 区域 | list、form、dialog、action |
action|status|label |
具体语义 | submit、loading、title |
// 示例
'order.list.title': '订单列表',
'order.form.submit': '提交订单',
'order.status.pending': '待处理',
'order.dialog.deleteConfirm': '确认删除该订单?',
'user.profile.edit': '编辑资料',
键冲突预防:
// ❌ 冲突:'order.edit' 同时是叶子节点和父节点前缀
'order.edit': '编辑',
'order.edit.title': '编辑订单',
// ✅ 修正:给叶子节点加上语义后缀
'order.edit.action': '编辑',
'order.edit.title': '编辑订单',
参数化翻译:
// 语言包
'order.total': '共 {count} 件商品,合计 {amount}',
'user.greeting': '你好,{name}',
// 组件中使用
t('order.total', { count: 5, amount: '¥100.00' })
t('user.greeting', { name: user.name })
标准命名空间(根据项目模块预定义,禁止随意创建):
| 命名空间 | 用途 | 文件 |
|---|---|---|
common |
通用 UI(按钮、状态、提示) | common.ts |
auth |
登录/注册/权限 | auth.ts |
menu |
侧边栏/导航菜单 | menu.ts |
validation |
表单校验提示 | validation.ts |
error |
错误码/错误提示 | error.ts |
{module} |
各业务模块 (order/user/...) | {module}.ts |
新增命名空间时必须在此表中登记。
5. 组件中使用
<script setup>
import { useI18n } from 'vue-i18n'
const { t, locale } = useI18n()
function switchLanguage(lang) {
locale.value = lang
localStorage.setItem('locale', lang)
}
</script>
<template>
<!-- 管理端 -->
<el-button @click="switchLanguage('en')">English</el-button>
<el-button @click="switchLanguage('zh-CN')">中文</el-button>
<!-- 用户端(禁止 Element Plus) -->
<button class="px-3 py-1 rounded border" @click="switchLanguage('en')">English</button>
<button class="px-3 py-1 rounded border" @click="switchLanguage('zh-CN')">中文</button>
<!-- 基础用法 -->
<p>{{ t('common.action.save') }}</p>
<!-- 参数化翻译 -->
<p>{{ t('order.total', { count: orderCount, amount: totalAmount }) }}</p>
<!-- 管理端:在 Element Plus 组件中使用 -->
<el-button type="primary">{{ t('common.action.save') }}</el-button>
<el-input :placeholder="t('order.form.searchPlaceholder')" />
<!-- 用户端:在 Tailwind 组件中使用 -->
<button class="bg-blue-600 text-white px-4 py-2 rounded-lg">{{ t('common.action.save') }}</button>
<input class="border rounded-lg px-3 py-2" :placeholder="t('order.form.searchPlaceholder')" />
</template>
6. Element Plus 国际化(仅管理端)
以下内容仅适用于
Case-Database-Frontend-admin/,用户端无需配置 Element Plus 国际化。
main.ts 不在 setup 上下文中,无法使用 computed 做响应式切换。正确方案:main.ts 静态初始化,App.vue 中通过 computed + ElConfigProvider 动态响应:
// src/main.ts
import ElementPlus from 'element-plus'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
import en from 'element-plus/es/locale/lang/en'
// 初始化时根据 locale 选择语言包
const elLocaleMap = { 'zh-CN': zhCn, en }
const initLocale = localStorage.getItem('locale') || 'zh-CN'
app.use(ElementPlus, { locale: elLocaleMap[initLocale] ?? zhCn })
<!-- 在根组件 App.vue 中动态响应语言切换 -->
<script setup>
import { useI18n } from 'vue-i18n'
import { computed } from 'vue'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
import en from 'element-plus/es/locale/lang/en'
const { locale } = useI18n()
const elLocaleMap = { 'zh-CN': zhCn, en }
// 通过 ElConfigProvider 的 locale prop 动态切换
const elLocale = computed(() => elLocaleMap[locale.value] ?? zhCn)
</script>
<template>
<el-config-provider :locale="elLocale">
<router-view />
</el-config-provider>
</template>
7. 添加新语言
- 在
src/locales/下创建新的 locale JS 文件(如fr.ts) - 在
src/locales/index.ts中注册新语言 - 翻译所有已有 key
- 管理端:同步 Element Plus 对应的 locale(用户端跳过此步)
验证
- 所有 locale 文件的 key 结构一致(无遗漏)
- 切换语言后页面正确翻译
- 管理端:Element Plus 组件语言同步切换(用户端不适用)
- 日期/数字格式随 locale 变化
- 键命名遵循
{feature}.{context}.{action|status|label}公式 - 使用扁平 dot notation,不使用嵌套对象
- 无键冲突(同一前缀不同时作为叶子节点和父节点前缀)
- 参数化翻译使用
{variableName}语法 - 新命名空间已在标准命名空间表中登记