初始化
This commit is contained in:
253
.cursor/skills/i18n/SKILL.md
Normal file
253
.cursor/skills/i18n/SKILL.md
Normal file
@@ -0,0 +1,253 @@
|
||||
---
|
||||
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. [ ] 新命名空间已在标准命名空间表中登记
|
||||
Reference in New Issue
Block a user