- 在门诊增强页面添加表格列的国际化标签 - 在门诊增强页面添加操作按钮的国际化文本 - 在门诊财务日结结算页面实现表单字段的国际化 - 在门诊财务日结结算页面实现表格列标题的国际化 - 在门诊财务日结结算页面添加操作按钮的国际化 - 集成 vue-i18n 并在组件中使用国际化方法 - 更新日结详情提示消息为国际化文本 - 实现导出文件名和成功消息的国际化
654 lines
22 KiB
Markdown
654 lines
22 KiB
Markdown
# HealthLink-HIS 多语言(i18n)技术方案
|
|
|
|
> **文档类型**: 架构设计
|
|
> **版本**: V1.0
|
|
> **日期**: 2026-06-24
|
|
> **作者**: zhugeliang(架构师)
|
|
> **状态**: 待确认
|
|
|
|
---
|
|
|
|
## 一、现状分析
|
|
|
|
### 1.1 后端现状
|
|
|
|
| 项目 | 值 |
|
|
|------|------|
|
|
| Spring Boot 版本 | 4.0.6 |
|
|
| 已有 i18n 基础 | `I18nConfig` (SessionLocaleResolver + LocaleChangeInterceptor, lang参数) |
|
|
| 已有 i18n 资源 | `i18n/messages.properties` + `i18n/messages_zh_CN.properties` (56行) |
|
|
| MessageUtils | 已封装 `MessageUtils.message(code, args)` 静态方法 |
|
|
| 消息键体系 | `PromptMsgConstant` (150+ key, 10个模块接口) |
|
|
| 硬编码中文错误 | **~851处** `R.fail("中文")` 散落在各 Controller/AppService |
|
|
| 数据库枚举值 | 状态值以数字存储, 前端映射显示 |
|
|
|
|
**核心问题**:
|
|
- 后端已有 i18n 基础设施, 但只用了 ~56 条消息, 远不够覆盖
|
|
- 大量业务错误消息硬编码为中文字符串 (`R.fail("中文")`)
|
|
- `PromptMsgConstant` 有 150+ 消息键, 但只有 zh_CN 翻译
|
|
|
|
### 1.2 前端现状
|
|
|
|
| 项目 | 值 |
|
|
|------|------|
|
|
| 技术栈 | Vue 3 + Vite + Element Plus + Pinia |
|
|
| 页面数量 | ~200+ `.vue` 文件 |
|
|
| ElMessage/ElNotification 调用 | **~2109处** (仅 views/) |
|
|
| 路由标题 | `meta.title` 硬编码中文 |
|
|
| 侧边栏/菜单 | `meta.title` 从数据库动态加载 |
|
|
| 布局组件 | Navbar/Sidebar/Settings 中有硬编码中文 |
|
|
| Element Plus 组件 | el-dialog title, el-button label, el-table column label 等 |
|
|
| 已有 i18n 方案 | **无** (未安装 vue-i18n) |
|
|
|
|
**核心问题**:
|
|
- 零 i18n 基础, 所有中文硬编码在 .vue 文件中
|
|
- Element Plus 自身也是中文 (需配置 locale)
|
|
- 菜单标题来自数据库, 需要数据库层面的多语言支持
|
|
|
|
### 1.3 移动端现状
|
|
|
|
| 项目 | 值 |
|
|
|------|------|
|
|
| 技术栈 | Vue 3 + Vite + Element Plus |
|
|
| 页面数量 | 14 个 .vue 文件 |
|
|
| 已有 i18n 方案 | **无** |
|
|
|
|
---
|
|
|
|
## 二、方案设计
|
|
|
|
### 2.1 总体架构
|
|
|
|
```
|
|
┌─────────────────────────────────────┐
|
|
│ 语言选择器 (全局) │
|
|
│ Cookie/Session 存储当前语言 │
|
|
└──────────┬──────────────────────────┘
|
|
│
|
|
┌────────────────┼────────────────┐
|
|
▼ ▼ ▼
|
|
┌───────────┐ ┌──────────────┐ ┌──────────────┐
|
|
│ zh_CN (默认)│ │ en_US (英语) │ │ vi_VN (越南语) │
|
|
└───────────┘ └──────────────┘ └──────────────┘
|
|
│ │ │
|
|
┌─────┴────────────────┴────────────────┴─────┐
|
|
│ │
|
|
┌────┴────┐ ┌──────┴──────┐
|
|
│ 前端 i18n │ │ 后端 i18n │
|
|
│ vue-i18n │ │ MessageSource│
|
|
└────┬────┘ └──────┬──────┘
|
|
│ │
|
|
▼ ▼
|
|
JSON 语言包文件 Properties 资源文件
|
|
(src/locales/) (i18n/messages_*.properties)
|
|
```
|
|
|
|
### 2.2 核心技术选型
|
|
|
|
#### 前端
|
|
|
|
| 方案 | 选择 | 理由 |
|
|
|------|------|------|
|
|
| i18n 库 | **vue-i18n v9** | Vue 3 官方推荐, 生态成熟, 与 Element Plus 完美集成 |
|
|
| 语言包格式 | **JSON** | 易编辑、易翻译、易从数据库导出、易做 CI/CD 检查 |
|
|
| 语言切换 | **Cookie** | 持久化, 刷新不丢失, 与后端 SessionLocaleResolver 保持一致 |
|
|
| Element Plus | **动态切换 locale** | `el-config-provider` + `setLocale` API |
|
|
|
|
#### 后端
|
|
|
|
| 方案 | 选择 | 理由 |
|
|
|------|------|------|
|
|
| i18n 引擎 | **Spring MessageSource (已有)** | 无需新增依赖, 已有 `I18nConfig` + `MessageUtils` |
|
|
| 资源文件格式 | **Properties** | Spring Boot 原生支持, 已有基础 |
|
|
| 语言切换 | **Cookie/Header (新增)** | 保持向后兼容, 同时支持 `lang` query param |
|
|
|
|
#### 数据库 (菜单/字典多语言)
|
|
|
|
| 方案 | 选择 | 理由 |
|
|
|------|------|------|
|
|
| 菜单多语言 | **新建 `sys_menu_i18n` 表** | 不修改已有 `sys_menu` 表结构, 零侵入 |
|
|
| 字典多语言 | **新建 `sys_dict_type_i18n` + `sys_dict_data_i18n` 表** | 字典数据量大, 独立表便于管理 |
|
|
| 业务数据多语言 | **暂不支持** | 患者名、诊断名等业务数据不需多语言 |
|
|
|
|
---
|
|
|
|
## 三、详细设计
|
|
|
|
### 3.1 前端实现
|
|
|
|
#### 3.1.1 目录结构
|
|
|
|
```
|
|
healthlink-his-ui/src/
|
|
├── locales/ # 语言包目录
|
|
│ ├── index.js # vue-i18n 配置 + 导出
|
|
│ ├── zh_CN.js # 简体中文 (基于现有硬编码提取)
|
|
│ ├── en_US.js # English
|
|
│ └── vi_VN.js # Tiếng Việt
|
|
├── plugins/
|
|
│ └── i18n.js # vue-i18n 插件初始化
|
|
├── store/modules/
|
|
│ ├── app.js # ← 新增: language 状态
|
|
│ └── settings.js # ← 新增: language 配置
|
|
├── layout/
|
|
│ └── components/
|
|
│ └── LanguageSwitcher.vue # 新增: 语言切换下拉组件
|
|
├── utils/
|
|
│ └── request.js # ← 修改: 请求头带 Accept-Language
|
|
└── ...views/** # ← 批量修改: 硬编码中文 → $t('key')
|
|
```
|
|
|
|
#### 3.1.2 语言包结构设计
|
|
|
|
```javascript
|
|
// locales/zh_CN.js 结构示例
|
|
export default {
|
|
// 系统通用
|
|
common: {
|
|
confirm: '确认',
|
|
cancel: '取消',
|
|
save: '保存',
|
|
delete: '删除',
|
|
edit: '编辑',
|
|
add: '新增',
|
|
search: '搜索',
|
|
export: '导出',
|
|
import: '导入',
|
|
reset: '重置',
|
|
submit: '提交',
|
|
close: '关闭',
|
|
tip: '提示',
|
|
success: '操作成功',
|
|
fail: '操作失败',
|
|
loading: '加载中...',
|
|
noData: '暂无数据',
|
|
},
|
|
|
|
// 登录/认证
|
|
auth: {
|
|
login: '登录',
|
|
username: '用户名',
|
|
password: '密码',
|
|
captcha: '验证码',
|
|
loginSuccess: '登录成功',
|
|
logout: '退出登录',
|
|
tokenExpired: '登录已过期,请重新登录',
|
|
},
|
|
|
|
// 系统管理
|
|
system: {
|
|
userManage: '用户管理',
|
|
roleManage: '角色管理',
|
|
menuManage: '菜单管理',
|
|
deptManage: '部门管理',
|
|
dictManage: '字典管理',
|
|
},
|
|
|
|
// 门诊挂号
|
|
registration: {
|
|
outpatientReg: '门诊挂号',
|
|
patientSearch: '患者检索',
|
|
registerSuccess: '挂号成功',
|
|
refund: '退号',
|
|
refundSuccess: '退号成功',
|
|
},
|
|
|
|
// 消息提示 (对应后端 PromptMsgConstant)
|
|
msgs: {
|
|
addSuccess: '{0}添加成功',
|
|
saveSuccess: '{0}保存成功',
|
|
deleteSuccess: '{0}删除成功',
|
|
operationSuccess: '{0}操作成功',
|
|
alreadyExists: '{0}已经存在',
|
|
operationFailed: '操作失败,请联系管理员',
|
|
dataDeletedByOthers: '操作失败,该数据已被他人删除,请刷新后重试',
|
|
dataModifiedByOthers: '操作失败,该数据已被他人更改,请刷新后重试',
|
|
noDuplicateSubmit: '请勿重复提交',
|
|
querySuccess: '查询成功',
|
|
},
|
|
|
|
// 菜单标题 (与 sys_menu 的 menu_name 对应, 用于前端硬编码部分)
|
|
menus: {
|
|
dashboard: '首页',
|
|
patientManage: '患者管理',
|
|
registration: '挂号收费',
|
|
outpatient: '门诊管理',
|
|
inpatient: '住院管理',
|
|
pharmacy: '药品管理',
|
|
// ... 所有菜单项
|
|
},
|
|
|
|
// Element Plus 组件
|
|
element: {
|
|
pagination: {
|
|
total: '共 {total} 条',
|
|
pageSize: '每页 {pageSize} 条',
|
|
validator: '每页 {n} 条,至少 1 条',
|
|
},
|
|
table: {
|
|
emptyText: '暂无数据',
|
|
},
|
|
dialog: {
|
|
confirm: '确定',
|
|
cancel: '取消',
|
|
},
|
|
upload: {
|
|
exceedSize: '上传文件大小超出限制',
|
|
fileNameExceedLength: '上传的文件名最长 {0} 个字符',
|
|
},
|
|
},
|
|
}
|
|
```
|
|
|
|
#### 3.1.3 前端改造策略
|
|
|
|
**Phase 1 — 基础设施 (不破坏现有功能)**
|
|
1. 安装 `vue-i18n@9`
|
|
2. 创建 `locales/` 目录和 `zh_CN.js` (先搬运现有中文)
|
|
3. 配置 `plugins/i18n.js`
|
|
4. 修改 `main.js` 引入 i18n
|
|
5. 修改 `request.js` 添加 `Accept-Language` 请求头
|
|
6. 新增 `LanguageSwitcher.vue` 组件
|
|
7. 修改 `app.js` store 增加 `language` 状态
|
|
|
|
**Phase 2 — 核心页面改造 (逐步推进)**
|
|
8. 登录页 `views/login.vue` → 全部 $t()
|
|
9. 布局组件 `layout/**` → 全部 $t()
|
|
10. 系统管理模块 `views/system/**` → 全部 $t()
|
|
11. 门诊模块 `views/charge/**`, `views/doctorstation/**` → 全部 $t()
|
|
12. 住院模块 `views/inpatientNurse/**` → 全部 $t()
|
|
13. 药品模块 `views/drug/**`, `views/pharmacy*/**` → 全部 $t()
|
|
14. 其余模块依次推进
|
|
|
|
**Phase 3 — 辅助功能**
|
|
15. Element Plus 组件语言包切换
|
|
16. 路由 meta.title 多语言化
|
|
17. 表单校验提示国际化 (Element Plus form rules)
|
|
|
|
#### 3.1.4 语言切换流程
|
|
|
|
```
|
|
用户点击 LanguageSwitcher
|
|
↓
|
|
Cookie.set('language', 'en_US')
|
|
↓
|
|
Pinia store.app.language = 'en_US'
|
|
↓
|
|
vue-i18n.global.locale.value = 'en_US'
|
|
↓
|
|
Element Plus setLocale(en)
|
|
↓
|
|
Axios 请求头: Accept-Language: en-US
|
|
↓
|
|
页面响应式更新 (所有 $t() 自动重新渲染)
|
|
```
|
|
|
|
### 3.2 后端实现
|
|
|
|
#### 3.2.1 资源文件扩展
|
|
|
|
```
|
|
healthlink-his-application/src/main/resources/i18n/
|
|
├── messages.properties # 默认 (英文 fallback)
|
|
├── messages_zh_CN.properties # 中文 (现有, 扩充)
|
|
├── messages_en_US.properties # 英文 (新建)
|
|
└── messages_vi_VN.properties # 越南语 (新建)
|
|
```
|
|
|
|
#### 3.2.2 消息分类体系
|
|
|
|
```
|
|
# 1. 系统通用 (已有, 扩充)
|
|
apl.common.M00001 = {0} added successfully
|
|
apl.common.M00002 = {0} saved successfully
|
|
...
|
|
|
|
# 2. 用户认证
|
|
auth.login.success = Login successful
|
|
auth.user.not.exists = User does not exist / Incorrect password
|
|
auth.password.retry.limit.count = Password entered incorrectly {0} times
|
|
...
|
|
|
|
# 3. 文件上传
|
|
upload.exceed.maxSize = Upload file size exceeds limit
|
|
...
|
|
|
|
# 4. 权限
|
|
no.permission = You do not have permission for [{0}]
|
|
...
|
|
|
|
# 5. 挂号收费 (registration/payment)
|
|
apl.payment.M00001 = Actual payment amount does not match expected
|
|
apl.payment.M00003 = Please select a payment method
|
|
...
|
|
|
|
# 6. 药品管理 (pharmacy)
|
|
apl.pharmacy.outOfStock = Insufficient inventory for {0}
|
|
apl.pharmacy.dispenseSuccess = Dispensing successful
|
|
...
|
|
|
|
# 7. 住院管理 (inpatient)
|
|
...
|
|
|
|
# 8. 检查检验 (check/lab)
|
|
...
|
|
|
|
# 9. 手术麻醉 (surgery/anesthesia)
|
|
...
|
|
|
|
# 10. 电子病历 (emr)
|
|
...
|
|
|
|
# 11. 护理管理 (nursing)
|
|
...
|
|
|
|
# 12. 医保 (yb)
|
|
...
|
|
```
|
|
|
|
#### 3.2.3 硬编码消息迁移策略
|
|
|
|
**优先级 P0 — 高频路径** (登录、挂号、退号、缴费、退药)
|
|
- `SysLoginService.java` — 登录相关 ~6处
|
|
- `OutpatientRegistrationAppServiceImpl.java` — 挂号/退号 ~10处
|
|
- `PaymentRecServiceImpl.java` — 收费/退费 ~30处
|
|
|
|
**优先级 P1 — 核心业务** (药品、住院、手术、EMR)
|
|
- 药房 dispensense/approval ~50处
|
|
- 住院登记/结算 ~20处
|
|
- 手术管理 ~15处
|
|
- EMR 病程记录 ~10处
|
|
|
|
**优先级 P2 — 其他模块**
|
|
- 其余 ~700处按模块逐步迁移
|
|
|
|
**迁移方式**:
|
|
```java
|
|
// Before:
|
|
return R.fail("请选择调配药师");
|
|
|
|
// After:
|
|
return R.fail(MessageUtils.message("apl.pharmacy.selectDispenser"));
|
|
```
|
|
|
|
#### 3.2.4 语言检测优先级
|
|
|
|
```
|
|
1. Cookie "language" (最高优先级, 持久化)
|
|
2. HTTP Header "Accept-Language"
|
|
3. Query Param "lang" (已有, 向后兼容)
|
|
4. Session 中保存的语言
|
|
5. 默认 Locale.SIMPLIFIED_CHINESE
|
|
```
|
|
|
|
新增 `LanguageCookieLocaleResolver` 继承 `SessionLocaleResolver`:
|
|
|
|
```java
|
|
@Bean
|
|
public LocaleResolver localeResolver() {
|
|
CookieLocaleResolver clr = new CookieLocaleResolver("lang");
|
|
clr.setDefaultLocale(Constants.DEFAULT_LOCALE);
|
|
clr.setCookieMaxAge(Duration.ofDays(365));
|
|
return clr;
|
|
}
|
|
```
|
|
|
|
### 3.3 数据库多语言
|
|
|
|
#### 3.3.1 sys_menu_i18n 表
|
|
|
|
```sql
|
|
CREATE TABLE IF NOT EXISTS sys_menu_i18n (
|
|
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
|
|
menu_id BIGINT NOT NULL REFERENCES sys_menu(menu_id),
|
|
lang_code VARCHAR(10) NOT NULL, -- 'zh_CN', 'en_US', 'vi_VN'
|
|
menu_name VARCHAR(100) NOT NULL,
|
|
UNIQUE(menu_id, lang_code)
|
|
);
|
|
|
|
COMMENT ON TABLE sys_menu_i18n IS '菜单多语言表';
|
|
COMMENT ON COLUMN sys_menu_i18n.menu_id IS '关联 sys_menu.menu_id';
|
|
COMMENT ON COLUMN sys_menu_i18n.lang_code IS '语言代码';
|
|
COMMENT ON COLUMN sys_menu_i18n.menu_name IS '菜单名称(多语言)';
|
|
```
|
|
|
|
**查询逻辑**:
|
|
```sql
|
|
-- 优先取当前语言, 回退到中文, 再回退到默认
|
|
SELECT COALESCE(
|
|
(SELECT menu_name FROM sys_menu_i18n WHERE menu_id = m.menu_id AND lang_code = 'en_US'),
|
|
(SELECT menu_name FROM sys_menu_i18n WHERE menu_id = m.menu_id AND lang_code = 'zh_CN'),
|
|
m.menu_name
|
|
) AS menu_name
|
|
FROM sys_menu m;
|
|
```
|
|
|
|
#### 3.3.2 sys_dict_data_i18n 表
|
|
|
|
```sql
|
|
CREATE TABLE IF NOT EXISTS sys_dict_data_i18n (
|
|
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
|
|
dict_code BIGINT NOT NULL,
|
|
dict_sort INT NOT NULL,
|
|
lang_code VARCHAR(10) NOT NULL,
|
|
dict_label VARCHAR(100) NOT NULL,
|
|
dict_value VARCHAR(100) NOT NULL,
|
|
css_class VARCHAR(100),
|
|
list_class VARCHAR(100),
|
|
is_default CHAR(1) DEFAULT 'N',
|
|
UNIQUE(dict_code, dict_sort, lang_code)
|
|
);
|
|
|
|
COMMENT ON TABLE sys_dict_data_i18n IS '字典数据多语言表';
|
|
```
|
|
|
|
#### 3.3.3 字典类型 i18n
|
|
|
|
```sql
|
|
CREATE TABLE IF NOT EXISTS sys_dict_type_i18n (
|
|
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
|
|
dict_id BIGINT NOT NULL REFERENCES sys_dict_type(dict_id),
|
|
lang_code VARCHAR(10) NOT NULL,
|
|
dict_name VARCHAR(100) NOT NULL,
|
|
dict_type VARCHAR(100) NOT NULL,
|
|
UNIQUE(dict_id, lang_code)
|
|
);
|
|
|
|
COMMENT ON TABLE sys_dict_type_i18n IS '字典类型多语言表';
|
|
```
|
|
|
|
---
|
|
|
|
## 四、实施计划
|
|
|
|
### Sprint 1: 基础设施搭建 (预计 2-3 天)
|
|
|
|
| 任务 | 模块 | 工作量 | 负责人 |
|
|
|------|------|--------|--------|
|
|
| T1.1 安装 vue-i18n@9 | 前端 | 0.5h | 赵云 |
|
|
| T1.2 创建 locales/ 目录结构 + zh_CN.js | 前端 | 2h | 赵云 |
|
|
| T1.3 配置 plugins/i18n.js + main.js 集成 | 前端 | 1h | 赵云 |
|
|
| T1.4 扩展后端 messages_zh_CN.properties | 后端 | 2h | 关羽 |
|
|
| T1.5 创建 messages_en_US.properties (翻译 P0 消息) | 后端 | 3h | 关羽 |
|
|
| T1.6 创建 messages_vi_VN.properties (翻译 P0 消息) | 后端 | 3h | 关羽 |
|
|
| T1.7 改造 I18nConfig 支持 Cookie + Header | 后端 | 1h | 关羽 |
|
|
| T1.8 新增 LanguageSwitcher.vue 组件 | 前端 | 1h | 赵云 |
|
|
| T1.9 修改 request.js 添加 Accept-Language | 前端 | 0.5h | 赵云 |
|
|
| T1.10 编译验证 + 冒烟测试 | 全栈 | 1h | 张飞 |
|
|
|
|
**产出**: 语言切换功能可用, 登录页/布局组件中英文切换
|
|
|
|
### Sprint 2: 核心模块国际化 (预计 3-4 天)
|
|
|
|
| 任务 | 模块 | 工作量 | 负责人 |
|
|
|------|------|--------|--------|
|
|
| T2.1 登录页 login.vue 全部 $t() | 前端 | 2h | 赵云 |
|
|
| T2.2 布局组件 (Navbar/Sidebar/Settings) 全部 $t() | 前端 | 3h | 赵云 |
|
|
| T2.3 Element Plus 组件语言包切换 | 前端 | 2h | 赵云 |
|
|
| T2.4 系统管理模块 views/system/** 全部 $t() | 前端 | 4h | 赵云 |
|
|
| T2.5 后端 P0 消息全部翻译 (auth/payment/upload/permission) | 后端 | 3h | 关羽 |
|
|
| T2.6 后端硬编码迁移 P0 (SysLoginService, Registration, Payment) | 后端 | 4h | 关羽 |
|
|
| T2.7 Flyway 迁移: sys_menu_i18n + sys_dict_*_i18n 表 | DBA | 1h | 荀彧 |
|
|
| T2.8 菜单/字典 Service 层支持多语言查询 | 后端 | 3h | 关羽 |
|
|
| T2.9 编译验证 + 冒烟测试 | 全栈 | 1h | 张飞 |
|
|
|
|
**产出**: 系统管理 + 登录 + 布局 中英文/越语可用
|
|
|
|
### Sprint 3: 业务模块国际化 (预计 5-7 天)
|
|
|
|
| 任务 | 模块 | 工作量 | 负责人 |
|
|
|------|------|--------|--------|
|
|
| T3.1 门诊模块 (charge/doctorstation) 全部 $t() | 前端 | 6h | 赵云 |
|
|
| T3.2 门诊模块后端硬编码迁移 | 后端 | 4h | 关羽 |
|
|
| T3.3 门诊模块消息翻译 (en_US + vi_VN) | 后端 | 4h | 关羽 |
|
|
| T3.4 住院模块 (inpatientNurse) 全部 $t() | 前端 | 5h | 赵云 |
|
|
| T3.5 住院模块后端硬编码迁移 | 后端 | 3h | 关羽 |
|
|
| T3.6 住院模块消息翻译 (en_US + vi_VN) | 后端 | 3h | 关羽 |
|
|
| T3.7 药品模块 (drug/pharmacy) 全部 $t() | 前端 | 5h | 赵云 |
|
|
| T3.8 药品模块后端硬编码迁移 | 后端 | 4h | 关羽 |
|
|
| T3.9 药品模块消息翻译 (en_US + vi_VN) | 后端 | 3h | 关羽 |
|
|
| T3.10 EMR/病历模块 全部 $t() | 前端 | 4h | 赵云 |
|
|
| T3.11 EMR 后端硬编码迁移 | 后端 | 2h | 关羽 |
|
|
| T3.12 EMR 消息翻译 (en_US + vi_VN) | 后端 | 2h | 关羽 |
|
|
| T3.13 其余模块 (检验/检查/手术/护理/质控...) 全部 $t() | 前端 | 10h | 赵云 |
|
|
| T3.14 其余模块后端硬编码迁移 | 后端 | 8h | 关羽 |
|
|
| T3.15 其余模块消息翻译 (en_US + vi_VN) | 后端 | 8h | 关羽 |
|
|
| T3.16 路由 meta.title 多语言化 | 前端 | 2h | 赵云 |
|
|
| T3.17 表单校验提示国际化 | 前端 | 2h | 赵云 |
|
|
| T3.18 编译验证 + 全模块冒烟测试 | 全栈 | 2h | 张飞 |
|
|
|
|
**产出**: 全模块中英文/越语可用
|
|
|
|
### Sprint 4: 移动端 + 验收 (预计 2-3 天)
|
|
|
|
| 任务 | 模块 | 工作量 | 负责人 |
|
|
|------|------|--------|--------|
|
|
| T4.1 移动端 healthlink-his-mobile 安装 vue-i18n | 移动端 | 0.5h | 赵云 |
|
|
| T4.2 移动端 14 个页面全部 $t() | 移动端 | 4h | 赵云 |
|
|
| T4.3 移动端语言切换器 | 移动端 | 1h | 赵云 |
|
|
| T4.4 全模块端到端测试 (中文→英文→越语) | 测试 | 2h | 张飞 |
|
|
| T4.5 性能测试 (语言切换不卡顿) | 测试 | 1h | 张飞 |
|
|
| T4.6 验收 + 文档更新 | 华佗 | 1h | 华佗 |
|
|
|
|
---
|
|
|
|
## 五、工作量估算
|
|
|
|
| 阶段 | 前端 | 后端 | DBA | 测试 | 总计 |
|
|
|------|------|------|-----|------|------|
|
|
| Sprint 1 | 5h | 10h | 0 | 0 | 15h |
|
|
| Sprint 2 | 12h | 11h | 1h | 0 | 24h |
|
|
| Sprint 3 | 37h | 29h | 0 | 0 | 66h |
|
|
| Sprint 4 | 6h | 0 | 0 | 3h | 9h |
|
|
| **合计** | **60h** | **50h** | **1h** | **3h** | **~114h (约 2-3 周)** |
|
|
|
|
---
|
|
|
|
## 六、风险与应对
|
|
|
|
| 风险 | 影响 | 应对 |
|
|
|------|------|------|
|
|
| 2109+ 处前端硬编码修改量大 | 前端工作量大 | 分批推进, 优先核心模块, 非核心可延后 |
|
|
| 851+ 处后端硬编码迁移 | 后端工作量大 | 按模块优先级分批迁移, P0→P1→P2 |
|
|
| 数据库菜单/字典数据量大 | 迁移困难 | 提供 SQL 脚本自动从现有数据初始化 |
|
|
| 翻译质量 (越南语) | 用户体验差 | 先机器翻译, 上线后逐步人工校对 |
|
|
| 语言包文件过大 | 加载性能 | 按需加载 (lazy load), 分包加载 |
|
|
| 第三方组件未国际化 | 部分中文残留 | Element Plus 已支持, 其他按需处理 |
|
|
| 动态路由菜单多语言 | 需改后端 | 菜单查询 SQL 增加 COALESCE 回退逻辑 |
|
|
|
|
---
|
|
|
|
## 七、扩展性设计
|
|
|
|
### 7.1 新增语言只需 3 步
|
|
|
|
1. 创建 `locales/{langCode}.js` (前端) + `messages_{langCode}.properties` (后端)
|
|
2. 在 `locales/index.js` 注册新语言
|
|
3. 在 `I18nConfig` 中确认默认值 (无需改动)
|
|
|
|
### 7.2 翻译管理工具 (可选增强)
|
|
|
|
后续可接入:
|
|
- **Crowdin / Lokalise**: 在线翻译协作平台
|
|
- **xliff-js**: 导出/导入 XLIFF 格式
|
|
- 自定义脚本: 从 `.vue` 文件自动提取所有中文, 生成翻译模板
|
|
|
|
### 7.3 数据库多语言扩展
|
|
|
|
如需其他业务表多语言 (如科室名称、诊断名称), 可按相同模式扩展:
|
|
```sql
|
|
-- 通用多语言表模板
|
|
CREATE TABLE {table}_i18n (
|
|
id BIGINT PRIMARY KEY,
|
|
source_id BIGINT NOT NULL,
|
|
lang_code VARCHAR(10) NOT NULL,
|
|
field_name VARCHAR(100) NOT NULL,
|
|
field_value TEXT NOT NULL,
|
|
UNIQUE(source_id, lang_code, field_name)
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
## 八、验证标准
|
|
|
|
### 8.1 功能验证
|
|
|
|
- [ ] 语言切换器正常显示 (中/英/越)
|
|
- [ ] 切换后页面所有中文文本变为对应语言
|
|
- [ ] 刷新页面后语言设置保持 (Cookie)
|
|
- [ ] Element Plus 组件 (分页/弹窗/表格/表单) 均为对应语言
|
|
- [ ] 后端 API 返回的错误消息为当前语言
|
|
- [ ] 登录页、系统管理、门诊、住院、药品、EMR 等核心模块全部覆盖
|
|
- [ ] 移动端 14 个页面全部支持多语言
|
|
|
|
### 8.2 兼容性验证
|
|
|
|
- [ ] 不传 lang 参数时默认中文 (向后兼容)
|
|
- [ ] 现有前端页面不因 i18n 改造而样式错乱
|
|
- [ ] 数据库菜单查询多语言回退正常 (无翻译 → 显示中文 → 显示默认)
|
|
- [ ] 编译无 ERROR, 无新增 WARNING
|
|
|
|
### 8.3 性能验证
|
|
|
|
- [ ] 语言切换响应 < 100ms
|
|
- [ ] 首屏加载时间增加 < 50KB (gzip)
|
|
- [ ] 无明显内存泄漏
|
|
|
|
---
|
|
|
|
## 九、附录
|
|
|
|
### A. 前端需提取的中文文本类型
|
|
|
|
1. **ElMessage** 提示文本 (~2109处)
|
|
2. **ElMessageBox** 确认/提示文本
|
|
3. **el-dialog** title 属性
|
|
4. **el-button** 文本内容
|
|
5. **el-table** column label
|
|
6. **el-form** label / placeholder / 校验提示
|
|
7. **路由 meta.title** (部分硬编码)
|
|
8. **布局组件** 中的固定文本 (Sidebar/Navbar/Settings)
|
|
9. **Element Plus** 自身组件文本 (pagination/table/dialog)
|
|
10. **页面标题** h1/h2 等
|
|
|
|
### B. 后端需迁移的硬编码消息类型
|
|
|
|
1. **R.fail("中文")** — 业务错误 (~851处)
|
|
2. **R.ok(data, "中文")** — 成功消息 (~150处, 已有 PromptMsgConstant 覆盖)
|
|
3. **Service 层抛出的 ServiceException** — 异常消息
|
|
4. **日志中的中文** — 可选, 生产日志建议英文
|
|
|
|
### C. 消息键命名规范
|
|
|
|
```
|
|
{模块}.{子类}.{编号}
|
|
|
|
示例:
|
|
apl.common.M00001 → APL 通用消息
|
|
auth.login.success → 认证-登录-成功
|
|
payment.collect.fail → 收费-缴费-失败
|
|
pharmacy.dispense.select_pharmacist → 药品-发药-选择药师
|
|
registration.refund.success → 挂号-退号-成功
|
|
```
|