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

254 lines
7.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: i18n
version: 3.0.0
description: "使用 vue-i18n 管理 Vue 3 应用的国际化。当需要添加多语言支持或管理翻译时使用。含语言包管理和路由国际化。"
---
> ⚠️ 核心执行流程已在 `.cursor/rules/skill-i18n.mdc` 中由 Cursor 自动注入。
> 本文件提供完整模板、代码示例和边缘场景处理,供 Agent 按需深入 Read。
# Internationalization (i18n) — Vue 3 + vue-i18n
## 触发条件
用户要求添加多语言支持、翻译内容、配置国际化路由。
## 执行流程
### 1. 检测现有 i18n 方案
```bash
# 检查是否已有 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 官方国际化方案)
```bash
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 配置
```typescript
// 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
```
```typescript
// src/main.ts
import i18n from './locales'
app.use(i18n)
```
### 4. 翻译键命名规范
**使用扁平 dot notation**(不使用嵌套对象),避免深层路径冲突:
```typescript
// ✅ 扁平 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` |
```typescript
// 示例
'order.list.title': '订单列表',
'order.form.submit': '提交订单',
'order.status.pending': '待处理',
'order.dialog.deleteConfirm': '确认删除该订单?',
'user.profile.edit': '编辑资料',
```
**键冲突预防**
```typescript
// ❌ 冲突:'order.edit' 同时是叶子节点和父节点前缀
'order.edit': '编辑',
'order.edit.title': '编辑订单',
// ✅ 修正:给叶子节点加上语义后缀
'order.edit.action': '编辑',
'order.edit.title': '编辑订单',
```
**参数化翻译**
```typescript
// 语言包
'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. 组件中使用
```vue
<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` 动态响应:
```typescript
// 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 })
```
```vue
<!-- 在根组件 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. [ ] 新命名空间已在标准命名空间表中登记