Files
vibe_coding/.cursor/skills/i18n/SKILL.md
2026-03-05 21:27:11 +08:00

7.5 KiB
Raw Blame History

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-i18nVue 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 业务功能/模块 orderusercommon
context 所在 UI 区域 listformdialogaction
action|status|label 具体语义 submitloadingtitle
// 示例
'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. 添加新语言

  1. src/locales/ 下创建新的 locale JS 文件(如 fr.ts
  2. src/locales/index.ts 中注册新语言
  3. 翻译所有已有 key
  4. 管理端:同步 Element Plus 对应的 locale用户端跳过此步

验证

  1. 所有 locale 文件的 key 结构一致(无遗漏)
  2. 切换语言后页面正确翻译
  3. 管理端Element Plus 组件语言同步切换(用户端不适用)
  4. 日期/数字格式随 locale 变化
  5. 键命名遵循 {feature}.{context}.{action|status|label} 公式
  6. 使用扁平 dot notation不使用嵌套对象
  7. 无键冲突(同一前缀不同时作为叶子节点和父节点前缀)
  8. 参数化翻译使用 {variableName} 语法
  9. 新命名空间已在标准命名空间表中登记