|
|
@@ -1 +1,316 @@
|
|
|
-node scripts/extract-i18n.js 文件路径,详细到.vue/.ts
|
|
|
+## 项目中英文转换逻辑说明
|
|
|
+
|
|
|
+### 一、国际化(i18n)架构概述
|
|
|
+
|
|
|
+本项目采用 **Vue I18n** 实现中英文双语切换,核心架构包含配置层、语言包层和组件调用层三个层次。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### 二、核心文件结构
|
|
|
+
|
|
|
+| 文件路径 | 功能说明 |
|
|
|
+|---------|---------|
|
|
|
+| `src/i18n.ts` | Vue I18n 核心配置,注册语言包 |
|
|
|
+| `src/i18n.d.ts` | TypeScript 类型定义文件 |
|
|
|
+| `src/locales/zh.json` | 中文语言包(键值对存储) |
|
|
|
+| `src/locales/en.json` | 英文语言包(与中文键结构一致) |
|
|
|
+| `src/locales/index.js` | 备用语言管理模块 |
|
|
|
+| `scripts/extract-i18n.js` | 自动提取国际化标记的脚本 |
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### 三、中英文转换核心逻辑
|
|
|
+
|
|
|
+#### 3.1 配置初始化 (`src/i18n.ts`)
|
|
|
+
|
|
|
+```typescript
|
|
|
+import { createI18n } from 'vue-i18n';
|
|
|
+import zh from './locales/zh.json';
|
|
|
+import en from './locales/en.json';
|
|
|
+
|
|
|
+const i18n = createI18n({
|
|
|
+legacy: false, // 使用 Composition API 模式
|
|
|
+locale: 'zh', // 默认语言:中文
|
|
|
+fallbackLocale: 'en', // 回退语言:英文
|
|
|
+messages: { zh, en }, // 语言包映射对象
|
|
|
+});
|
|
|
+
|
|
|
+export default i18n;
|
|
|
+```
|
|
|
+
|
|
|
+**关键配置说明:**
|
|
|
+- `legacy: false` - 启用 Vue 3 Composition API 兼容模式
|
|
|
+- `locale` - 当前活跃语言标识
|
|
|
+- `fallbackLocale` - 当某个 key 在当前语言包不存在时,使用回退语言
|
|
|
+
|
|
|
+#### 3.2 语言包结构
|
|
|
+
|
|
|
+语言包采用**嵌套 JSON 结构**,支持层级键路径:
|
|
|
+
|
|
|
+```json
|
|
|
+// src/locales/zh.json
|
|
|
+{
|
|
|
+"Header": {
|
|
|
+ "title": "土壤酸化智能预测专家系统",
|
|
|
+ "welcome": "欢迎",
|
|
|
+ "logout": "退出登录"
|
|
|
+},
|
|
|
+"login": {
|
|
|
+ "userTitle": "用户登录",
|
|
|
+ "loginButton": "登录",
|
|
|
+ "loginFailed": "登录失败,请检查用户名或密码"
|
|
|
+},
|
|
|
+"validation": {
|
|
|
+ "usernameRequired": "请输入账号",
|
|
|
+ "passwordLength": "密码长度为 3 到 16 个字符"
|
|
|
+}
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+#### 3.3 组件调用方式
|
|
|
+
|
|
|
+**Vue 模板中的使用:**
|
|
|
+
|
|
|
+```vue
|
|
|
+<template>
|
|
|
+<!-- 直接使用 t() 函数 -->
|
|
|
+<h2>{{ t('login.userTitle') }}</h2>
|
|
|
+
|
|
|
+<!-- 表单验证 -->
|
|
|
+<el-input :placeholder="t('validation.usernameRequired')" />
|
|
|
+
|
|
|
+<!-- 动态文本 -->
|
|
|
+<el-button>{{ t('login.loginButton') }}</el-button>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts">
|
|
|
+import { useI18n } from 'vue-i18n';
|
|
|
+
|
|
|
+const { t, locale } = useI18n();
|
|
|
+
|
|
|
+// 切换语言
|
|
|
+const toggleLanguage = () => {
|
|
|
+locale.value = locale.value === 'zh' ? 'en' : 'zh';
|
|
|
+localStorage.setItem('lang', locale.value);
|
|
|
+};
|
|
|
+</script>
|
|
|
+```
|
|
|
+
|
|
|
+**TypeScript 文件中的使用:**
|
|
|
+
|
|
|
+```typescript
|
|
|
+// 在组合式函数中使用
|
|
|
+import { useI18n } from 'vue-i18n';
|
|
|
+
|
|
|
+const { t } = useI18n();
|
|
|
+
|
|
|
+// 用于消息提示
|
|
|
+ElMessage.success(t('login.loginSuccess'));
|
|
|
+
|
|
|
+// 用于动态文本
|
|
|
+const errorMsg = t('login.loginFailed');
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### 四、语言切换流程
|
|
|
+
|
|
|
+#### 4.1 完整切换流程(以登录页为例)
|
|
|
+
|
|
|
+```typescript
|
|
|
+// src/views/login/loginView.vue
|
|
|
+const toggleLanguage = async () => {
|
|
|
+// 1. 计算新语言
|
|
|
+const newLocale = locale.value === "zh" ? "en" : "zh";
|
|
|
+
|
|
|
+// 2. 更新 Vue I18n locale(触发视图自动更新)
|
|
|
+locale.value = newLocale;
|
|
|
+
|
|
|
+// 3. 持久化到 localStorage(刷新页面保持语言设置)
|
|
|
+localStorage.setItem("lang", newLocale);
|
|
|
+
|
|
|
+// 4. 可选:同步到后端(保存用户语言偏好)
|
|
|
+try {
|
|
|
+ await changeLanguageAPI({
|
|
|
+ language: newLocale,
|
|
|
+ userId: store.userInfo?.userId,
|
|
|
+ timestamp: Date.now(),
|
|
|
+ });
|
|
|
+} catch (error) {
|
|
|
+ console.error("语言切换同步失败:", error);
|
|
|
+}
|
|
|
+};
|
|
|
+```
|
|
|
+
|
|
|
+#### 4.2 初始化时的语言恢复
|
|
|
+
|
|
|
+在应用启动时,从 localStorage 读取上次保存的语言:
|
|
|
+
|
|
|
+```typescript
|
|
|
+// src/main.ts
|
|
|
+const savedLang = localStorage.getItem('lang') || 'zh';
|
|
|
+const i18n = createI18n({
|
|
|
+locale: savedLang,
|
|
|
+// ... 其他配置
|
|
|
+});
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### 五、国际化标记提取脚本
|
|
|
+
|
|
|
+#### 5.1 脚本位置和功能
|
|
|
+
|
|
|
+**脚本文件:** `scripts/extract-i18n.js`
|
|
|
+
|
|
|
+**核心功能:**
|
|
|
+- 自动扫描 `.vue` 和 `.ts` 文件
|
|
|
+- 识别特定标记格式的中文文本
|
|
|
+- 自动生成/更新语言包文件
|
|
|
+
|
|
|
+#### 5.2 使用方法
|
|
|
+
|
|
|
+```bash
|
|
|
+# 扫描整个 src 目录(默认)
|
|
|
+node scripts/extract-i18n.js
|
|
|
+
|
|
|
+# 扫描指定目录
|
|
|
+node scripts/extract-i18n.js src/views/User
|
|
|
+
|
|
|
+# 扫描单个文件
|
|
|
+node scripts/extract-i18n.js src/components/layout/AppLayout.vue
|
|
|
+```
|
|
|
+
|
|
|
+#### 5.3 提取标记格式
|
|
|
+
|
|
|
+在 Vue/TS 文件中使用以下格式标记需要国际化的文本:
|
|
|
+
|
|
|
+```vue
|
|
|
+<!--i18n:模块名.键名-->中文文本内容
|
|
|
+```
|
|
|
+
|
|
|
+**示例:**
|
|
|
+
|
|
|
+```vue
|
|
|
+<!--i18n:Header.title-->土壤酸化智能预测专家系统
|
|
|
+<!--i18n:login.userTitle-->用户登录
|
|
|
+<!--i18n:Menu.dataManagement-->数据管理
|
|
|
+```
|
|
|
+
|
|
|
+#### 5.4 脚本处理流程
|
|
|
+
|
|
|
+```
|
|
|
+1. 解析命令行参数 → 确定扫描路径
|
|
|
+2. 读取已有语言包 → 保留历史翻译
|
|
|
+3. 递归扫描文件 → 收集 .vue/.ts 文件
|
|
|
+4. 正则匹配标记 → 提取键名和中文文本
|
|
|
+5. 构建嵌套对象 → 处理键路径(如 Menu.subMenu.item)
|
|
|
+6. 写入语言包 → 更新 zh.json 和 en.json
|
|
|
+```
|
|
|
+
|
|
|
+#### 5.5 脚本核心代码解析
|
|
|
+
|
|
|
+**标记匹配正则:**
|
|
|
+```javascript
|
|
|
+const regex = /<!--i18n:(\S+)-->([\u4e00-\u9fa5\w\s,.,。;;!!??::()()]+?)(?=\r?\n|['"<]|$)/g;
|
|
|
+```
|
|
|
+
|
|
|
+**嵌套键设置函数:**
|
|
|
+```javascript
|
|
|
+function setNestedValue(obj, keyPath, value) {
|
|
|
+const keys = keyPath.split('.');
|
|
|
+let current = obj;
|
|
|
+
|
|
|
+for (let i = 0; i < keys.length - 1; i++) {
|
|
|
+ const key = keys[i];
|
|
|
+ if (typeof current[key] === 'string') {
|
|
|
+ console.warn(`键名冲突:"${key}" 已作为字符串存在`);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (!current[key] || typeof current[key] !== 'object') {
|
|
|
+ current[key] = {};
|
|
|
+ }
|
|
|
+ current = current[key];
|
|
|
+}
|
|
|
+
|
|
|
+const lastKey = keys[keys.length - 1];
|
|
|
+if (current[lastKey] === undefined) {
|
|
|
+ current[lastKey] = value;
|
|
|
+}
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### 六、语言包维护规范
|
|
|
+
|
|
|
+| 规范项 | 说明 |
|
|
|
+|-------|------|
|
|
|
+| **键命名** | 使用 `模块名.子模块.键名` 格式,如 `Menu.dataManagement` |
|
|
|
+| **结构对齐** | 中英文语言包必须保持**完全相同的键结构** |
|
|
|
+| **值处理** | 中文包存储中文原文,英文包存储对应英文翻译 |
|
|
|
+| **新增翻译** | 同时在 `zh.json` 和 `en.json` 中添加键值对 |
|
|
|
+| **标记规范** | 使用 `<!--i18n:键名-->` 标记便于脚本提取 |
|
|
|
+| **避免硬编码** | 所有用户可见文本必须通过 `t()` 函数获取 |
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### 七、现有翻译覆盖范围
|
|
|
+
|
|
|
+| 模块 | 说明 |
|
|
|
+|-----|------|
|
|
|
+| **Header** | 顶部导航栏文本 |
|
|
|
+| **login/register/logout** | 登录、注册、退出相关 |
|
|
|
+| **Menu** | 侧边栏菜单项 |
|
|
|
+| **validation** | 表单验证提示 |
|
|
|
+| **AcidModelMap** | 酸化模型地图相关 |
|
|
|
+| **PhPrediction** | pH 预测模型相关 |
|
|
|
+| **Calculation** | 计算模块相关 |
|
|
|
+| **DetectionStatistics** | 检测统计相关 |
|
|
|
+| **SoilCdStatistics** | 土壤镉统计相关 |
|
|
|
+| **LandCultivatedStatistics** | 耕地统计相关 |
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### 八、Element Plus 组件国际化
|
|
|
+
|
|
|
+Element Plus 组件默认使用中文,如需支持英文需额外配置:
|
|
|
+
|
|
|
+```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';
|
|
|
+
|
|
|
+// 根据当前语言动态切换 Element Plus 语言
|
|
|
+app.use(ElementPlus, {
|
|
|
+locale: locale.value === 'zh' ? zhCn : en,
|
|
|
+});
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### 九、扩展说明
|
|
|
+
|
|
|
+#### 9.1 添加新翻译流程
|
|
|
+
|
|
|
+1. **标记文本**:在 Vue/TS 文件中添加 `<!--i18n:键名-->` 标记
|
|
|
+2. **运行脚本**:执行 `node scripts/extract-i18n.js` 提取标记
|
|
|
+3. **翻译英文**:在 `en.json` 中填写对应英文翻译
|
|
|
+4. **使用翻译**:在组件中使用 `t('键名')` 获取文本
|
|
|
+
|
|
|
+#### 9.2 语言切换事件通知
|
|
|
+
|
|
|
+通过自定义事件通知其他组件语言变化:
|
|
|
+
|
|
|
+```typescript
|
|
|
+// 触发语言切换事件
|
|
|
+window.dispatchEvent(new CustomEvent('languageChanged', {
|
|
|
+detail: { locale: locale.value }
|
|
|
+}));
|
|
|
+
|
|
|
+// 监听语言切换事件
|
|
|
+window.addEventListener('languageChanged', (e) => {
|
|
|
+console.log('语言已切换为:', e.detail.locale);
|
|
|
+});
|
|
|
+```
|