feat(i18n): translate backend error messages, dict labels auto-translate, add 150+ medical dict terms

This commit is contained in:
2026-06-25 21:15:44 +08:00
parent 7c7b02225d
commit 4e2e11292f
4 changed files with 152 additions and 17 deletions

View File

@@ -260,7 +260,96 @@ const translationDict = {
"ESB": "ESB",
"FHIR": "FHIR",
"LIS": "LIS",
"PACS": "PACS"
"PACS": "PACS",
// 常见字典标签
"一类": "Type I", "二类": "Type II", "三类": "Type III", "四类": "Type IV",
"全麻": "General Anesthesia", "局麻": "Local Anesthesia", "椎管内麻醉": "Spinal Anesthesia",
"神经阻滞": "Nerve Block", "复合麻醉": "Combined Anesthesia",
"甲": "Class A", "乙": "Class B", "丙": "Class C",
"自费": "Self-Pay", "公费": "Public Funded", "医保": "Medical Insurance",
"现金": "Cash", "微信": "WeChat", "支付宝": "Alipay", "银行卡": "Bank Card",
"医保卡": "Insurance Card", "扫码支付": "QR Payment",
"初诊": "First Visit", "复诊": "Follow-up Visit", "急诊": "Emergency",
"普通号": "Regular", "专家号": "Specialist",
"门诊": "Outpatient", "住院": "Inpatient",
"待诊": "Waiting", "就诊中": "In Consultation", "已完成": "Completed",
"已签到": "Checked In", "已预约": "Booked", "已退号": "Cancelled",
"男性": "Male", "女性": "Female",
"已婚": "Married", "未婚": "Single", "离异": "Divorced", "丧偶": "Widowed",
"汉族": "Han", "回族": "Hui", "藏族": "Tibetan", "维吾尔族": "Uyghur",
"A型": "Type A", "B型": "Type B", "AB型": "Type AB", "O型": "Type O",
"Rh阳性": "Rh+", "Rh阴性": "Rh-",
"抗生素": "Antibiotic", "非抗生素": "Non-Antibiotic",
"限制使用": "Restricted", "非限制使用": "Unrestricted", "特殊使用": "Special Use",
"口服": "Oral", "注射": "Injection", "外用": "Topical", "吸入": "Inhalation",
"静脉注射": "IV", "肌肉注射": "IM", "皮下注射": "SC", "静脉滴注": "IV Drip",
"每日一次": "Once Daily", "每日两次": "Twice Daily", "每日三次": "Three Times Daily",
"必要时": "As Needed", "立即": "Immediately", "每晚": "Every Night",
"急诊手术": "Emergency Surgery", "择期手术": "Elective Surgery",
"一级手术": "Grade I", "二级手术": "Grade II", "三级手术": "Grade III", "四级手术": "Grade IV",
"Ⅰ级": "Grade I", "Ⅱ级": "Grade II", "Ⅲ级": "Grade III", "Ⅳ级": "Grade IV",
"切口甲级": "Grade A Incision", "切口乙级": "Grade B Incision", "切口丙级": "Grade C Incision",
"甲级愈合": "Grade A Healing", "乙级愈合": "Grade B Healing", "丙级愈合": "Grade C Healing",
"清醒": "Awake", "嗜睡": "Drowsy", "昏迷": "Coma", "模糊": "Confused",
"普通病房": "General Ward", "ICU": "ICU", "手术室": "Operating Room",
"已入院": "Admitted", "已出院": "Discharged", "转科": "Transfer",
"待执行": "Pending", "执行中": "In Progress", "已执行": "Executed", "已停止": "Stopped",
"长期医嘱": "Long-term Order", "临时医嘱": "Temporary Order",
"待签发": "Pending Sign", "已签发": "Signed", "待校对": "Pending Proofread", "已校对": "Proofread",
"阳性": "Positive", "阴性": "Negative",
"合格": "Qualified", "不合格": "Unqualified",
"空气中": "Air", "物体表面": "Surface", "手卫生": "Hand Hygiene",
"消毒液": "Disinfectant", "医疗器械": "Medical Device",
"红细胞悬液": "RBC Suspension", "血浆": "Plasma", "血小板": "Platelet", "冷沉淀": "Cryoprecipitate", "全血": "Whole Blood",
"输血前": "Pre-Transfusion", "输血中": "During Transfusion", "输血后": "Post-Transfusion",
"一级预警": "Level 1 Alert", "二级预警": "Level 2 Alert", "三级预警": "Level 3 Alert",
"黄色预警": "Yellow Alert", "红色预警": "Red Alert",
"呼吸道传染病": "Respiratory", "肠道传染病": "Intestinal", "血源性传染病": "Blood-borne",
"轻度": "Mild", "中度": "Moderate", "重度": "Severe",
"待处理": "Pending", "处理中": "Processing", "已处理": "Handled", "已排除": "Excluded",
"未隔离": "Not Isolated", "已隔离": "Isolated", "已解除": "Released",
"Ⅰ级 濒死": "Level I Critical", "Ⅱ级 危重": "Level II Emergent",
"Ⅲ级 急症": "Level III Urgent", "Ⅳ级 亚急": "Level IV Less Urgent", "Ⅴ级 非紧急": "Level V Non-Urgent",
"抢救区": "Rescue Zone", "留观区": "Observation Zone", "诊室": "Consult Room", "普通区": "General Zone",
"成功": "Success", "失败": "Failed", "进行中": "In Progress",
"住院": "Admit", "出院": "Discharge", "转科": "Transfer", "死亡": "Death",
"达标": "Achieved", "未达标": "Not Achieved",
"空气": "Air", "表面": "Surface", "手": "Hand",
"一级愈合": "Grade A", "二级愈合": "Grade B", "三级愈合": "Grade C",
"已挂号": "Registered", "进行中": "In Progress", "已退号": "Cancelled",
"日班": "Day Shift", "夜班": "Night Shift", "值班": "On Duty",
"主任医师": "Chief Physician", "副主任医师": "Associate Chief Physician",
"主治医师": "Attending Physician", "住院医师": "Resident",
"实习医师": "Intern", "进修医师": "Fellow",
"护士长": "Head Nurse", "主管护师": "Supervisor Nurse", "护师": "Nurse", "护士": "Junior Nurse",
"药品": "Drug", "诊疗": "Treatment", "耗材": "Consumable", "其他": "Other",
"西药": "Western Medicine", "中成药": "Chinese Patent Medicine", "中草药": "Chinese Herbal Medicine",
"注射液": "Injection", "片剂": "Tablet", "胶囊": "Capsule", "颗粒": "Granule",
"口服液": "Oral Liquid", "软膏": "Ointment", "滴眼液": "Eye Drops",
"国产": "Domestic", "进口": "Imported",
"甲类": "Class A", "乙类": "Class B", "丙类": "Class C",
"医保内": "Insurance Covered", "医保外": "Not Covered",
"集中采购": "Centralized Procurement", "自主采购": "Self Procurement",
"紧急采购": "Emergency Procurement", "常规采购": "Routine Procurement",
"入库": "Inbound", "出库": "Outbound", "调拨": "Transfer", "盘点": "Stocktaking",
"盈": "Profit", "亏": "Loss", "损": "Damage",
"门诊收费": "OPD Charge", "住院收费": "IPD Charge", "预交金": "Prepayment",
"已收费": "Charged", "未收费": "Uncharged", "已退费": "Refunded",
"待审批": "Pending Approval", "已审批": "Approved", "已驳回": "Rejected",
"电子处方": "E-Prescription", "纸质处方": "Paper Prescription",
"门诊处方": "OPD Prescription", "住院处方": "IPD Prescription",
"药品不良反应": "Drug ADR", "医疗器械不良事件": "Medical Device Adverse Event",
"新发": "New", "在治": "Under Treatment", "治愈": "Cured", "好转": "Improved", "未愈": "Not Cured", "死亡": "Death",
"门诊会诊": "OPD Consultation", "院内会诊": "In-hospital Consultation", "院际会诊": "Inter-hospital Consultation",
"急会诊": "Urgent Consultation", "普通会诊": "Routine Consultation",
"传染病报告": "Infectious Disease Report", "死亡报告": "Death Report",
"出生报告": "Birth Report", "肿瘤报告": "Tumor Report",
"Ⅰ类切口": "Class I Incision", "Ⅱ类切口": "Class II Incision", "Ⅲ类切口": "Class III Incision",
"无菌物品": "Sterile Items", "消毒物品": "Disinfected Items",
"手卫生依从": "HH Compliance", "手卫生不依从": "HH Non-Compliance",
"接触患者前": "Before Patient Contact", "接触患者后": "After Patient Contact",
"无菌操作前": "Before Aseptic Procedure", "体液暴露后": "After Body Fluid Exposure",
"接触患者周围环境后": "After Contact with Patient Environment"
},
'vi': {
"新增": "Thêm",

View File

@@ -1,8 +1,10 @@
import useDictStore from '@/store/modules/dict'
import {getDicts} from '@/api/system/dict/data'
import {autoTranslate} from '@/i18n/autoTranslate'
import Cookies from 'js-cookie'
/**
* 获取字典数据
* 获取字典数据(支持多语言自动翻译)
*/
export function useDict(...args) {
const res = ref({});
@@ -14,7 +16,14 @@ export function useDict(...args) {
res.value[dictType] = dicts;
} else {
getDicts(dictType).then(resp => {
res.value[dictType] = resp.data.map(p => ({ label: p.dictLabel, value: p.dictValue, elTagType: p.listClass, elTagClass: p.cssClass, remark: p.remark }))
const lang = Cookies.get('lang') || localStorage.getItem('lang') || 'zh-CN'
res.value[dictType] = resp.data.map(p => ({
label: lang === 'zh-CN' ? p.dictLabel : autoTranslate(p.dictLabel),
value: p.dictValue,
elTagType: p.listClass,
elTagClass: p.cssClass,
remark: p.remark
}))
useDictStore().setDict(dictType, res.value[dictType]);
})
}

View File

@@ -4,3 +4,19 @@ export default {
'404': '访问资源不存在',
'default': '系统未知错误,请反馈给管理员'
}
// English translations for error codes
export const errorCodeEn = {
'401': 'Authentication failed, cannot access system resources',
'403': 'No permission for current operation',
'404': 'Requested resource not found',
'default': 'Unknown system error, please contact administrator'
}
// Vietnamese translations for error codes
export const errorCodeVi = {
'401': 'Xác thực thất bại, không thể truy cập tài nguyên hệ thống',
'403': 'Không có quyền cho thao tác hiện tại',
'404': 'Tài nguyên yêu cầu không tồn tại',
'default': 'Lỗi hệ thống không xác định, vui lòng liên hệ quản trị viên'
}

View File

@@ -1,12 +1,14 @@
import axios from 'axios'
import {ElLoading, ElMessage, ElMessageBox, ElNotification} from 'element-plus'
import {getToken} from '@/utils/auth'
import errorCode from '@/utils/errorCode'
import errorCode, {errorCodeEn, errorCodeVi} from '@/utils/errorCode'
import {blobValidate, tansParams} from '@/utils/his'
import cache from '@/plugins/cache'
import {saveAs} from 'file-saver'
import useUserStore from '@/store/modules/user'
import JSONBig from 'json-bigint'
import {autoTranslate} from '@/i18n/autoTranslate'
import Cookies from 'js-cookie'
// 初始化json-bigint配置大数字转字符串关键storeAsString: true
const jsonBig = JSONBig({ storeAsString: true })
@@ -141,8 +143,14 @@ service.interceptors.request.use(config => {
service.interceptors.response.use(res => {
// 未设置状态码则默认成功状态
const code = res.data.code || 200;
// 获取错误信息
const msg = errorCode[code] || res.data.msg || errorCode['default']
// 获取错误信息(支持多语言)
const lang = Cookies.get('lang') || localStorage.getItem('lang') || 'zh-CN'
const errorDict = lang === 'en' ? errorCodeEn : lang === 'vi' ? errorCodeVi : errorCode
let msg = errorDict[code] || res.data.msg || errorDict['default']
// 后端返回的中文错误消息自动翻译
if (lang !== 'zh-CN' && /[\u4e00-\u9fff]/.test(msg)) {
msg = autoTranslate(msg)
}
// 二进制数据则直接返回
if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') {
return res.data
@@ -160,11 +168,12 @@ service.interceptors.request.use(config => {
}).catch(() => {
isRelogin.show = false;
});
return Promise.reject('登录已过期,请重新登录。')
return Promise.reject(lang === 'zh-CN' ? '登录已过期,请重新登录。' : lang === 'en' ? 'Session expired, please log in again.' : 'Phiên hết hạn, vui lòng đăng nhập lại.')
}
// 其他页面:显示提示后自动跳转
ElMessage.warning('登录已过期,正在跳转到登录页面...');
const expiredMsg = lang === 'zh-CN' ? '登录已过期,正在跳转到登录页面...' : lang === 'en' ? 'Session expired, redirecting to login...' : 'Phiên hết hạn, đang chuyển đến trang đăng nhập...'
ElMessage.warning(expiredMsg);
useUserStore().logOut().then(() => {
isRelogin.show = false;
// 跳转到登录页保留当前路径作为redirect参数
@@ -204,13 +213,15 @@ service.interceptors.request.use(config => {
},
error => {
console.log('err' + error)
const lang = Cookies.get('lang') || localStorage.getItem('lang') || 'zh-CN'
let { message } = error;
if (message == "Network Error") {
message = "后端接口连接异常";
message = lang === 'zh-CN' ? '后端接口连接异常' : lang === 'en' ? 'Backend API connection error' : 'Lỗi kết nối API backend';
} else if (message.includes("timeout")) {
message = "系统接口请求超时";
message = lang === 'zh-CN' ? '系统接口请求超时' : lang === 'en' ? 'API request timeout' : 'Yêu cầu API hết thời gian';
} else if (message.includes("Request failed with status code")) {
message = "系统接口" + message.substr(message.length - 3) + "异常";
const code = message.substr(message.length - 3)
message = lang === 'zh-CN' ? '系统接口' + code + '异常' : lang === 'en' ? 'API error ' + code : 'Lỗi API ' + code;
}
// 检查是否需要跳过错误提示
if (!error.config?.skipErrorMsg) {
@@ -221,7 +232,9 @@ service.interceptors.request.use(config => {
// 通用下载方法
export function download(url, params, filename, config) {
downloadLoadingInstance = ElLoading.service({ text: "正在下载数据,请稍候", background: "rgba(0, 0, 0, 0.7)", })
const lang = Cookies.get('lang') || localStorage.getItem('lang') || 'zh-CN'
const dlMsg = lang === 'zh-CN' ? '正在下载数据,请稍候' : lang === 'en' ? 'Downloading data, please wait' : 'Đang tải dữ liệu, vui lòng đợi'
downloadLoadingInstance = ElLoading.service({ text: dlMsg, background: "rgba(0, 0, 0, 0.7)", })
return service.post(url, params, {
transformRequest: [(params) => { return tansParams(params) }],
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
@@ -235,20 +248,25 @@ export function download(url, params, filename, config) {
} else {
const resText = await data.text();
const rspObj = JSON.parse(resText);
const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']
const errorDict = lang === 'en' ? errorCodeEn : lang === 'vi' ? errorCodeVi : errorCode
let errMsg = errorDict[rspObj.code] || rspObj.msg || errorDict['default']
if (lang !== 'zh-CN' && /[\u4e00-\u9fff]/.test(errMsg)) errMsg = autoTranslate(errMsg)
ElMessage.error(errMsg);
}
downloadLoadingInstance.close();
}).catch((r) => {
console.error(r)
ElMessage.error('下载文件出现错误,请联系管理员!')
const dlErr = lang === 'zh-CN' ? '下载文件出现错误,请联系管理员!' : lang === 'en' ? 'Download error, please contact administrator!' : 'Lỗi tải xuống, vui lòng liên hệ quản trị viên!'
ElMessage.error(dlErr)
downloadLoadingInstance.close();
})
}
// 添加GET方式下载方法
export function downloadGet(url, params, filename, config) {
downloadLoadingInstance = ElLoading.service({ text: "正在下载数据,请稍候", background: "rgba(0, 0, 0, 0.7)", })
const lang2 = Cookies.get('lang') || localStorage.getItem('lang') || 'zh-CN'
const dlMsg2 = lang2 === 'zh-CN' ? '正在下载数据,请稍候' : lang2 === 'en' ? 'Downloading data, please wait' : 'Đang tải dữ liệu, vui lòng đợi'
downloadLoadingInstance = ElLoading.service({ text: dlMsg2, background: "rgba(0, 0, 0, 0.7)", })
return service.get(url, {
params: params,
responseType: 'blob',
@@ -261,13 +279,16 @@ export function downloadGet(url, params, filename, config) {
} else {
const resText = await data.text();
const rspObj = JSON.parse(resText);
const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']
const errorDict2 = lang2 === 'en' ? errorCodeEn : lang2 === 'vi' ? errorCodeVi : errorCode
let errMsg = errorDict2[rspObj.code] || rspObj.msg || errorDict2['default']
if (lang2 !== 'zh-CN' && /[\u4e00-\u9fff]/.test(errMsg)) errMsg = autoTranslate(errMsg)
ElMessage.error(errMsg);
}
downloadLoadingInstance.close();
}).catch((r) => {
console.error(r)
ElMessage.error('下载文件出现错误,请联系管理员!')
const dlErr2 = lang2 === 'zh-CN' ? '下载文件出现错误,请联系管理员!' : lang2 === 'en' ? 'Download error, please contact administrator!' : 'Lỗi tải xuống, vui lòng liên hệ quản trị viên!'
ElMessage.error(dlErr2)
downloadLoadingInstance.close();
})
}