语言切换.txt 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. ## 项目中英文转换逻辑说明
  2. ### 一、国际化(i18n)架构概述
  3. 本项目采用 **Vue I18n** 实现中英文双语切换,核心架构包含配置层、语言包层和组件调用层三个层次。
  4. ---
  5. ### 二、核心文件结构
  6. | 文件路径 | 功能说明 |
  7. |---------|---------|
  8. | `src/i18n.ts` | Vue I18n 核心配置,注册语言包 |
  9. | `src/i18n.d.ts` | TypeScript 类型定义文件 |
  10. | `src/locales/zh.json` | 中文语言包(键值对存储) |
  11. | `src/locales/en.json` | 英文语言包(与中文键结构一致) |
  12. | `src/locales/index.js` | 备用语言管理模块 |
  13. | `scripts/extract-i18n.js` | 自动提取国际化标记的脚本 |
  14. ---
  15. ### 三、中英文转换核心逻辑
  16. #### 3.1 配置初始化 (`src/i18n.ts`)
  17. ```typescript
  18. import { createI18n } from 'vue-i18n';
  19. import zh from './locales/zh.json';
  20. import en from './locales/en.json';
  21. const i18n = createI18n({
  22. legacy: false, // 使用 Composition API 模式
  23. locale: 'zh', // 默认语言:中文
  24. fallbackLocale: 'en', // 回退语言:英文
  25. messages: { zh, en }, // 语言包映射对象
  26. });
  27. export default i18n;
  28. ```
  29. **关键配置说明:**
  30. - `legacy: false` - 启用 Vue 3 Composition API 兼容模式
  31. - `locale` - 当前活跃语言标识
  32. - `fallbackLocale` - 当某个 key 在当前语言包不存在时,使用回退语言
  33. #### 3.2 语言包结构
  34. 语言包采用**嵌套 JSON 结构**,支持层级键路径:
  35. ```json
  36. // src/locales/zh.json
  37. {
  38. "Header": {
  39. "title": "土壤酸化智能预测专家系统",
  40. "welcome": "欢迎",
  41. "logout": "退出登录"
  42. },
  43. "login": {
  44. "userTitle": "用户登录",
  45. "loginButton": "登录",
  46. "loginFailed": "登录失败,请检查用户名或密码"
  47. },
  48. "validation": {
  49. "usernameRequired": "请输入账号",
  50. "passwordLength": "密码长度为 3 到 16 个字符"
  51. }
  52. }
  53. ```
  54. #### 3.3 组件调用方式
  55. **Vue 模板中的使用:**
  56. ```vue
  57. <template>
  58. <!-- 直接使用 t() 函数 -->
  59. <h2>{{ t('login.userTitle') }}</h2>
  60. <!-- 表单验证 -->
  61. <el-input :placeholder="t('validation.usernameRequired')" />
  62. <!-- 动态文本 -->
  63. <el-button>{{ t('login.loginButton') }}</el-button>
  64. </template>
  65. <script setup lang="ts">
  66. import { useI18n } from 'vue-i18n';
  67. const { t, locale } = useI18n();
  68. // 切换语言
  69. const toggleLanguage = () => {
  70. locale.value = locale.value === 'zh' ? 'en' : 'zh';
  71. localStorage.setItem('lang', locale.value);
  72. };
  73. </script>
  74. ```
  75. **TypeScript 文件中的使用:**
  76. ```typescript
  77. // 在组合式函数中使用
  78. import { useI18n } from 'vue-i18n';
  79. const { t } = useI18n();
  80. // 用于消息提示
  81. ElMessage.success(t('login.loginSuccess'));
  82. // 用于动态文本
  83. const errorMsg = t('login.loginFailed');
  84. ```
  85. ---
  86. ### 四、语言切换流程
  87. #### 4.1 完整切换流程(以登录页为例)
  88. ```typescript
  89. // src/views/login/loginView.vue
  90. const toggleLanguage = async () => {
  91. // 1. 计算新语言
  92. const newLocale = locale.value === "zh" ? "en" : "zh";
  93. // 2. 更新 Vue I18n locale(触发视图自动更新)
  94. locale.value = newLocale;
  95. // 3. 持久化到 localStorage(刷新页面保持语言设置)
  96. localStorage.setItem("lang", newLocale);
  97. // 4. 可选:同步到后端(保存用户语言偏好)
  98. try {
  99. await changeLanguageAPI({
  100. language: newLocale,
  101. userId: store.userInfo?.userId,
  102. timestamp: Date.now(),
  103. });
  104. } catch (error) {
  105. console.error("语言切换同步失败:", error);
  106. }
  107. };
  108. ```
  109. #### 4.2 初始化时的语言恢复
  110. 在应用启动时,从 localStorage 读取上次保存的语言:
  111. ```typescript
  112. // src/main.ts
  113. const savedLang = localStorage.getItem('lang') || 'zh';
  114. const i18n = createI18n({
  115. locale: savedLang,
  116. // ... 其他配置
  117. });
  118. ```
  119. ---
  120. ### 五、国际化标记提取脚本
  121. #### 5.1 脚本位置和功能
  122. **脚本文件:** `scripts/extract-i18n.js`
  123. **核心功能:**
  124. - 自动扫描 `.vue` 和 `.ts` 文件
  125. - 识别特定标记格式的中文文本
  126. - 自动生成/更新语言包文件
  127. #### 5.2 使用方法
  128. ```bash
  129. # 扫描整个 src 目录(默认)
  130. node scripts/extract-i18n.js
  131. # 扫描指定目录
  132. node scripts/extract-i18n.js src/views/User
  133. # 扫描单个文件
  134. node scripts/extract-i18n.js src/components/layout/AppLayout.vue
  135. ```
  136. #### 5.3 提取标记格式
  137. 在 Vue/TS 文件中使用以下格式标记需要国际化的文本:
  138. ```vue
  139. <!--i18n:模块名.键名-->中文文本内容
  140. ```
  141. **示例:**
  142. ```vue
  143. <!--i18n:Header.title-->土壤酸化智能预测专家系统
  144. <!--i18n:login.userTitle-->用户登录
  145. <!--i18n:Menu.dataManagement-->数据管理
  146. ```
  147. #### 5.4 脚本处理流程
  148. ```
  149. 1. 解析命令行参数 → 确定扫描路径
  150. 2. 读取已有语言包 → 保留历史翻译
  151. 3. 递归扫描文件 → 收集 .vue/.ts 文件
  152. 4. 正则匹配标记 → 提取键名和中文文本
  153. 5. 构建嵌套对象 → 处理键路径(如 Menu.subMenu.item)
  154. 6. 写入语言包 → 更新 zh.json 和 en.json
  155. ```
  156. #### 5.5 脚本核心代码解析
  157. **标记匹配正则:**
  158. ```javascript
  159. const regex = /<!--i18n:(\S+)-->([\u4e00-\u9fa5\w\s,.,。;;!!??::()()]+?)(?=\r?\n|['"<]|$)/g;
  160. ```
  161. **嵌套键设置函数:**
  162. ```javascript
  163. function setNestedValue(obj, keyPath, value) {
  164. const keys = keyPath.split('.');
  165. let current = obj;
  166. for (let i = 0; i < keys.length - 1; i++) {
  167. const key = keys[i];
  168. if (typeof current[key] === 'string') {
  169. console.warn(`键名冲突:"${key}" 已作为字符串存在`);
  170. return;
  171. }
  172. if (!current[key] || typeof current[key] !== 'object') {
  173. current[key] = {};
  174. }
  175. current = current[key];
  176. }
  177. const lastKey = keys[keys.length - 1];
  178. if (current[lastKey] === undefined) {
  179. current[lastKey] = value;
  180. }
  181. }
  182. ```
  183. ---
  184. ### 六、语言包维护规范
  185. | 规范项 | 说明 |
  186. |-------|------|
  187. | **键命名** | 使用 `模块名.子模块.键名` 格式,如 `Menu.dataManagement` |
  188. | **结构对齐** | 中英文语言包必须保持**完全相同的键结构** |
  189. | **值处理** | 中文包存储中文原文,英文包存储对应英文翻译 |
  190. | **新增翻译** | 同时在 `zh.json` 和 `en.json` 中添加键值对 |
  191. | **标记规范** | 使用 `<!--i18n:键名-->` 标记便于脚本提取 |
  192. | **避免硬编码** | 所有用户可见文本必须通过 `t()` 函数获取 |
  193. ---
  194. ### 七、现有翻译覆盖范围
  195. | 模块 | 说明 |
  196. |-----|------|
  197. | **Header** | 顶部导航栏文本 |
  198. | **login/register/logout** | 登录、注册、退出相关 |
  199. | **Menu** | 侧边栏菜单项 |
  200. | **validation** | 表单验证提示 |
  201. | **AcidModelMap** | 酸化模型地图相关 |
  202. | **PhPrediction** | pH 预测模型相关 |
  203. | **Calculation** | 计算模块相关 |
  204. | **DetectionStatistics** | 检测统计相关 |
  205. | **SoilCdStatistics** | 土壤镉统计相关 |
  206. | **LandCultivatedStatistics** | 耕地统计相关 |
  207. ---
  208. ### 八、Element Plus 组件国际化
  209. Element Plus 组件默认使用中文,如需支持英文需额外配置:
  210. ```typescript
  211. // src/main.ts
  212. import ElementPlus from 'element-plus';
  213. import zhCn from 'element-plus/es/locale/lang/zh-cn';
  214. import en from 'element-plus/es/locale/lang/en';
  215. // 根据当前语言动态切换 Element Plus 语言
  216. app.use(ElementPlus, {
  217. locale: locale.value === 'zh' ? zhCn : en,
  218. });
  219. ```
  220. ---
  221. ### 九、扩展说明
  222. #### 9.1 添加新翻译流程
  223. 1. **标记文本**:在 Vue/TS 文件中添加 `<!--i18n:键名-->` 标记
  224. 2. **运行脚本**:执行 `node scripts/extract-i18n.js` 提取标记
  225. 3. **翻译英文**:在 `en.json` 中填写对应英文翻译
  226. 4. **使用翻译**:在组件中使用 `t('键名')` 获取文本
  227. #### 9.2 语言切换事件通知
  228. 通过自定义事件通知其他组件语言变化:
  229. ```typescript
  230. // 触发语言切换事件
  231. window.dispatchEvent(new CustomEvent('languageChanged', {
  232. detail: { locale: locale.value }
  233. }));
  234. // 监听语言切换事件
  235. window.addEventListener('languageChanged', (e) => {
  236. console.log('语言已切换为:', e.detail.locale);
  237. });
  238. ```