|
|
@@ -0,0 +1,386 @@
|
|
|
+# 多语言资源的添加流程及实现原理
|
|
|
+
|
|
|
+## 概述
|
|
|
+
|
|
|
+本项目采用 React-Intl 库实现国际化(i18n)支持,通过 Redux 状态管理维护多语言状态。本文档详细介绍多语言资源的添加流程、技术实现原理以及相关开发规范。
|
|
|
+
|
|
|
+## 技术架构
|
|
|
+
|
|
|
+### 核心技术栈
|
|
|
+- **React-Intl v7.1.11**: React 国际化库
|
|
|
+- **Redux Toolkit**: 状态管理
|
|
|
+- **Ant Design**: UI 组件库(内置国际化支持)
|
|
|
+- **Taro**: 跨平台开发框架
|
|
|
+
|
|
|
+### 核心组件
|
|
|
+1. **IntlProvider**: React-Intl 上下文提供者
|
|
|
+2. **i18nSlice**: Redux 状态管理切片
|
|
|
+3. **语言切换组件**: LanguageSettingModal
|
|
|
+4. **转换脚本**: extract-i18n-json.js
|
|
|
+
|
|
|
+## 语言资源文件结构
|
|
|
+
|
|
|
+```
|
|
|
+src/assets/i18n/messages/
|
|
|
+├── zh.js # 中文语言资源
|
|
|
+└── en.js # 英文语言资源
|
|
|
+```
|
|
|
+
|
|
|
+### 文件格式
|
|
|
+每个语言文件都是标准的 ES6 模块:
|
|
|
+
|
|
|
+```javascript
|
|
|
+export default {
|
|
|
+ "greeting": "你好,世界!",
|
|
|
+ "login.username": "用户名",
|
|
|
+ "login.username.placeholder": "请输入用户名",
|
|
|
+ // ... 更多翻译项
|
|
|
+};
|
|
|
+```
|
|
|
+
|
|
|
+## 添加多语言资源的完整流程
|
|
|
+
|
|
|
+### 步骤1:代码开发阶段
|
|
|
+
|
|
|
+在组件中定义翻译键,并提供默认消息:
|
|
|
+
|
|
|
+```typescript
|
|
|
+import { FormattedMessage, useIntl } from 'react-intl';
|
|
|
+
|
|
|
+// 方法1:使用 FormattedMessage 组件
|
|
|
+<FormattedMessage
|
|
|
+ id="newFeature.title"
|
|
|
+ defaultMessage="新功能标题"
|
|
|
+/>
|
|
|
+
|
|
|
+// 方法2:使用 useIntl hook
|
|
|
+const intl = useIntl();
|
|
|
+const title = intl.formatMessage({
|
|
|
+ id: 'newFeature.title',
|
|
|
+ defaultMessage: '新功能标题'
|
|
|
+});
|
|
|
+```
|
|
|
+
|
|
|
+### 步骤2:添加翻译资源
|
|
|
+
|
|
|
+#### 2.1 在中文文件中添加翻译
|
|
|
+```javascript
|
|
|
+// src/assets/i18n/messages/zh.js
|
|
|
+export default {
|
|
|
+ // ... 现有翻译
|
|
|
+ "newFeature.title": "新功能标题",
|
|
|
+ "newFeature.description": "这是新功能的详细描述",
|
|
|
+};
|
|
|
+```
|
|
|
+
|
|
|
+#### 2.2 在英文文件中添加对应翻译
|
|
|
+```javascript
|
|
|
+// src/assets/i18n/messages/en.js
|
|
|
+export default {
|
|
|
+ // ... 现有翻译
|
|
|
+ "newFeature.title": "New Feature Title",
|
|
|
+ "newFeature.description": "This is the detailed description of the new feature",
|
|
|
+};
|
|
|
+```
|
|
|
+
|
|
|
+### 步骤3:转换并部署
|
|
|
+
|
|
|
+#### 3.1 执行转换脚本
|
|
|
+```bash
|
|
|
+node scripts/extract-i18n-json.js
|
|
|
+```
|
|
|
+
|
|
|
+脚本执行流程:
|
|
|
+1. 读取所有语言文件 (`zh.js`, `en.js`)
|
|
|
+2. 移除 `export default` 语法和结尾分号
|
|
|
+3. 转换为纯 JSON 格式
|
|
|
+4. 输出到 `scripts/output/i18n/` 目录
|
|
|
+
|
|
|
+转换后的文件格式:
|
|
|
+```json
|
|
|
+{
|
|
|
+ "greeting": "你好,世界!",
|
|
|
+ "login.username": "用户名",
|
|
|
+ "newFeature.title": "新功能标题"
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+#### 3.2 上传到服务器
|
|
|
+脚本执行后会自动询问服务器配置:
|
|
|
+
|
|
|
+```
|
|
|
+📤 SCP配置 (按Enter使用默认值):
|
|
|
+IP地址 (默认: 192.168.110.245):
|
|
|
+用户名 (默认: root):
|
|
|
+中文文件路径 (默认: /root/gydr/trans/zh_CN/zh.js):
|
|
|
+英文文件路径 (默认: /root/gydr/trans/en_US/en.js):
|
|
|
+```
|
|
|
+
|
|
|
+确认配置后自动通过 SCP 上传:
|
|
|
+```bash
|
|
|
+scp zh.js root@192.168.110.245:/root/gydr/trans/zh_CN/zh.js
|
|
|
+scp en.js root@192.168.110.245:/root/gydr/trans/en_US/en.js
|
|
|
+```
|
|
|
+
|
|
|
+## 实现原理
|
|
|
+
|
|
|
+### 1. React-Intl 集成原理
|
|
|
+
|
|
|
+#### 1.1 应用入口配置
|
|
|
+```typescript
|
|
|
+// src/app.tsx
|
|
|
+import { IntlProvider } from 'react-intl';
|
|
|
+
|
|
|
+<IntlProvider
|
|
|
+ locale={currentLocale ? currentLocale.split('_')[0] : 'en'}
|
|
|
+ messages={messages || {}}
|
|
|
+>
|
|
|
+ {/* 应用内容 */}
|
|
|
+</IntlProvider>
|
|
|
+```
|
|
|
+
|
|
|
+#### 1.2 状态管理架构
|
|
|
+```typescript
|
|
|
+// src/states/i18nSlice.ts
|
|
|
+interface I18nState {
|
|
|
+ messages: I18nMessages; // 当前语言的翻译字典
|
|
|
+ currentLocale: string; // 当前语言代码 (en_US/zh_CN)
|
|
|
+ supportedLocales: string[]; // 支持的语言列表
|
|
|
+ availableLanguages: LanguageItem[]; // 后端返回的语言选项
|
|
|
+ loading: boolean; // 加载状态
|
|
|
+ error: string | null; // 错误信息
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 2. 语言切换流程
|
|
|
+
|
|
|
+#### 2.1 用户操作流程
|
|
|
+1. 点击 SystemZone 中的"语言管理"按钮
|
|
|
+2. 弹出 LanguageSettingModal 对话框
|
|
|
+3. 自动加载可用语言列表
|
|
|
+4. 用户选择目标语言
|
|
|
+5. 调用后端 API 更新系统语言
|
|
|
+6. Redux store 更新当前语言
|
|
|
+7. 应用重新渲染显示新语言
|
|
|
+
|
|
|
+#### 2.2 API 接口
|
|
|
+```typescript
|
|
|
+// 获取语言列表
|
|
|
+GET /dr/api/v1/pub/language
|
|
|
+
|
|
|
+// 更新系统语言
|
|
|
+POST /api/v1/auth/manage/language
|
|
|
+{
|
|
|
+ "lang": "zh_CN.UTF-8" // 或 "en_US.UTF-8"
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 3. 转换脚本实现原理
|
|
|
+
|
|
|
+#### 3.1 核心转换逻辑
|
|
|
+```javascript
|
|
|
+function extractJsonFromJs(filePath) {
|
|
|
+ const content = fs.readFileSync(filePath, 'utf-8');
|
|
|
+
|
|
|
+ // 移除 export default 语句
|
|
|
+ let jsonString = content
|
|
|
+ .replace(/export\s+default\s+/g, '')
|
|
|
+ .trim();
|
|
|
+
|
|
|
+ // 移除末尾分号
|
|
|
+ if (jsonString.endsWith(';')) {
|
|
|
+ jsonString = jsonString.slice(0, -1);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 安全解析 JSON
|
|
|
+ const jsonObj = eval('(' + jsonString + ')');
|
|
|
+ return jsonObj;
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+#### 3.2 自动化部署流程
|
|
|
+脚本集成了完整的部署流程:
|
|
|
+1. 文件转换 (JS → JSON)
|
|
|
+2. 配置询问 (服务器信息)
|
|
|
+3. SCP 上传 (自动传输)
|
|
|
+4. 结果反馈 (成功/失败状态)
|
|
|
+
|
|
|
+## 翻译键命名规范
|
|
|
+
|
|
|
+### 命名规则
|
|
|
+采用点号分隔的多级命名结构,产品相关命名放在第一级:
|
|
|
+```
|
|
|
+[产品.]模块.子模块.元素[.状态]
|
|
|
+```
|
|
|
+
|
|
|
+### 产品层级命名
|
|
|
+根据产品类型在第一级添加产品标识符:
|
|
|
+
|
|
|
+#### 宠物产品 (VETDROS)
|
|
|
+- 第一级使用 `animal.` 前缀
|
|
|
+- 适用于宠物诊疗相关功能
|
|
|
+
|
|
|
+#### 人医产品 (DROS)
|
|
|
+- 无产品前缀,直接从模块开始
|
|
|
+- 适用于人类医疗相关功能
|
|
|
+
|
|
|
+### 实际示例
|
|
|
+
|
|
|
+#### 宠物产品示例
|
|
|
+- `animal.login.username` - 宠物产品登录模块用户名
|
|
|
+- `animal.register.patientId` - 宠物注册模块宠物编号
|
|
|
+- `animal.register.patientName.placeholder` - 宠物注册模块宠物昵称占位符
|
|
|
+- `animal.exam.action.resetParams` - 宠物检查模块重置参数动作
|
|
|
+
|
|
|
+#### 人医产品示例
|
|
|
+- `login.username` - 登录模块用户名
|
|
|
+- `register.patientId` - 注册模块患者编号
|
|
|
+- `register.patientName.placeholder` - 注册模块患者姓名占位符
|
|
|
+- `exam.action.resetParams` - 检查模块重置参数动作
|
|
|
+
|
|
|
+### 语义化分类
|
|
|
+
|
|
|
+#### 通用模块
|
|
|
+- `login.*` - 登录相关
|
|
|
+- `register.*` - 注册相关
|
|
|
+- `exam.*` - 检查相关
|
|
|
+- `worklist.*` - 工作清单相关
|
|
|
+- `output.*` - 输出相关
|
|
|
+- `actionPanel.*` - 操作面板相关
|
|
|
+
|
|
|
+#### 宠物专用模块
|
|
|
+- `animal.*` - 宠物相关功能
|
|
|
+- `animal.register.*` - 宠物注册
|
|
|
+- `animal.worklist.*` - 宠物工作清单
|
|
|
+- `animal.actionPanel.*` - 宠物操作面板
|
|
|
+
|
|
|
+#### 功能状态后缀
|
|
|
+- `.placeholder` - 输入框占位符
|
|
|
+- `.required` - 必填验证消息
|
|
|
+- `.success` - 成功提示
|
|
|
+- `.failed` - 失败提示
|
|
|
+- `.loading` - 加载状态
|
|
|
+- `.empty` - 空状态
|
|
|
+- `.error` - 错误状态
|
|
|
+
|
|
|
+### 命名原则
|
|
|
+1. **产品优先**: 宠物产品必须使用 `animal.` 前缀,人医产品直接从模块开始
|
|
|
+2. **模块清晰**: 使用具有语义的英文单词作为模块名
|
|
|
+3. **层次分明**: 通过点号建立清晰的层次结构
|
|
|
+4. **状态明确**: 对于不同状态的相同元素,使用状态后缀区分
|
|
|
+5. **一致性**: 相同功能在不同产品中保持相似的命名结构
|
|
|
+
|
|
|
+## 高级功能
|
|
|
+
|
|
|
+### 1. 参数化翻译
|
|
|
+```javascript
|
|
|
+// 消息文件
|
|
|
+"welcome.user": "欢迎,{name}!"
|
|
|
+
|
|
|
+// 使用方式
|
|
|
+<FormattedMessage
|
|
|
+ id="welcome.user"
|
|
|
+ values={{ name: userName }}
|
|
|
+/>
|
|
|
+```
|
|
|
+
|
|
|
+### 2. 复数处理
|
|
|
+```javascript
|
|
|
+// 消息文件
|
|
|
+"item.count": "有 {count, plural, =0 {没有项目} one {1 个项目} other {# 个项目}}"
|
|
|
+
|
|
|
+// 使用方式
|
|
|
+<FormattedMessage
|
|
|
+ id="item.count"
|
|
|
+ values={{ count: itemCount }}
|
|
|
+/>
|
|
|
+```
|
|
|
+
|
|
|
+### 3. 日期和数字格式化
|
|
|
+```typescript
|
|
|
+const intl = useIntl();
|
|
|
+
|
|
|
+// 日期格式化
|
|
|
+const formattedDate = intl.formatDate(date, {
|
|
|
+ year: 'numeric',
|
|
|
+ month: 'long',
|
|
|
+ day: 'numeric'
|
|
|
+});
|
|
|
+
|
|
|
+// 数字格式化
|
|
|
+const formattedNumber = intl.formatNumber(value, {
|
|
|
+ style: 'currency',
|
|
|
+ currency: 'CNY'
|
|
|
+});
|
|
|
+```
|
|
|
+
|
|
|
+## 最佳实践
|
|
|
+
|
|
|
+### 开发规范
|
|
|
+1. **默认消息**: 始终提供 `defaultMessage` 作为降级
|
|
|
+2. **键唯一性**: 确保翻译键在所有语言文件中唯一
|
|
|
+3. **一致性**: 中英文翻译键必须完全一致
|
|
|
+4. **模块化**: 按功能模块组织翻译键
|
|
|
+
|
|
|
+### 部署规范
|
|
|
+1. **环境隔离**: 不同环境使用不同服务器配置
|
|
|
+2. **备份策略**: 上传前备份现有翻译文件
|
|
|
+3. **回滚计划**: 准备翻译文件回滚方案
|
|
|
+4. **测试验证**: 部署后验证多语言切换功能
|
|
|
+
|
|
|
+## 常见问题及解决方案
|
|
|
+
|
|
|
+### 1. 翻译不显示
|
|
|
+**现象**: 界面显示翻译键而非翻译文本
|
|
|
+**原因**: 翻译键不存在或语言文件未正确加载
|
|
|
+**解决**:
|
|
|
+- 检查翻译键拼写是否正确
|
|
|
+- 验证所有语言文件是否包含该键
|
|
|
+- 确认语言切换是否生效
|
|
|
+
|
|
|
+### 2. IntlProvider 错误
|
|
|
+**现象**: "Could not find required `intl` object"
|
|
|
+**原因**: 组件渲染时 IntlProvider 还未挂载
|
|
|
+**解决**: 确保 IntlProvider 始终存在,提供默认值
|
|
|
+
|
|
|
+### 3. 转换脚本失败
|
|
|
+**现象**: 脚本执行报错
|
|
|
+**原因**: Node.js 环境问题或文件权限问题
|
|
|
+**解决**:
|
|
|
+- 检查 Node.js 版本
|
|
|
+- 验证文件路径和权限
|
|
|
+- 查看控制台错误信息
|
|
|
+
|
|
|
+### 4. 上传失败
|
|
|
+**现象**: SCP 上传失败
|
|
|
+**原因**: 网络连接或服务器配置问题
|
|
|
+**解决**:
|
|
|
+- 检查网络连接
|
|
|
+- 验证服务器地址和端口
|
|
|
+- 确认 SSH 密钥或密码正确
|
|
|
+
|
|
|
+## 扩展方向
|
|
|
+
|
|
|
+### 1. 自动化改进
|
|
|
+- 集成 CI/CD 流水线自动转换和上传
|
|
|
+- 添加翻译键重复检查
|
|
|
+- 实现翻译完成度统计
|
|
|
+
|
|
|
+### 2. 开发体验优化
|
|
|
+- VS Code 插件支持翻译键提示
|
|
|
+- 翻译键提取工具自动化生成
|
|
|
+- 实时翻译预览功能
|
|
|
+
|
|
|
+### 3. 翻译管理平台
|
|
|
+- 集成专业翻译管理工具
|
|
|
+- 支持翻译记忆库
|
|
|
+- 多版本翻译管理
|
|
|
+
|
|
|
+## 总结
|
|
|
+
|
|
|
+本项目的多语言实现方案提供了完整的开发到部署流程:
|
|
|
+1. **开发阶段**: 定义翻译键并添加默认消息
|
|
|
+2. **翻译阶段**: 在语言文件中添加对应翻译
|
|
|
+3. **部署阶段**: 执行转换脚本并上传到服务器
|
|
|
+
|
|
|
+通过标准化的流程和自动化工具,确保了多语言功能的开发效率和维护质量。开发者可以专注于业务功能开发,而翻译部署工作可以通过规范化的脚本完成。
|