const fs = require('fs'); const path = require('path'); const enUS = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'src', 'i18n', 'locales', 'enUS.json'), 'utf8')); const direct = { '第 {row} 行数据已保存': 'Row {row} data saved', '删除成功!已删除 1 个父行和 {count} 个子行': 'Deleted! Removed 1 parent row and {count} child rows', '加载{menu}数据失败,请检查网络或服务状态': 'Failed to load {menu} data, check network or service', '搜索到{count}条检查方法数据': 'Found {count} inspection method records', '搜索到{count}条检查部位数据': 'Found {count} inspection part records', '确定要删除这一行吗?': 'Delete this row?', '该操作将同时删除 {count} 个子行数据。': 'This will also delete {count} child rows.', '第 {page} 页': 'Page {page}', '卫生机构': 'Healthcare Org', '请选择机构': 'Select organization', '套餐级别': 'Package Level', '请选择套餐级别': 'Select package level', '套餐类别': 'Package Category', '请选择套餐类别': 'Select package category', '用户': 'User', '请输入用户名称': 'Enter user name', '服务费': 'Service Fee', '总金额': 'Total Amount', '组合套餐': 'Combo Package', '显示套餐名': 'Show package name', '启用标志': 'Enable Flag', '操作人': 'Operator', '旧编码格式,建议编辑套餐重新选择科室': 'Old code format, edit package to reselect dept', ' (旧格式)': ' (Old format)', '该套餐创建者未知,无法删除': 'Package creator unknown, cannot delete', '确认删除套餐ID:{id} - {name} 吗?删除后将无法恢复': 'Delete package ID:{id} - {name}? Cannot be undone.', '该套餐已被使用,无法删除': 'Package in use, cannot delete', '查看套餐': 'View Package', '用户选择': 'User Selection', '折扣': 'Discount', '制单人': 'Creator', '生成服务费': 'Generate Service Fee', '请选择用户': 'Select user', '请选择卫生机构': 'Select healthcare org', '自动计算': 'Auto Calculate', '请输入折扣': 'Enter discount', '自动合计': 'Auto Total', '请输入编号': 'Enter number', '输入名称/首字母/编号搜索': 'Search by name/initial/number', '自动获取': 'Auto Fetch', '不启用': 'Disabled', '编号': 'No.', '途径': 'Route', '频次': 'Frequency', '天数': 'Days', '产地': 'Origin', '添加': 'Add', '无匹配项目': 'No matching items', '请尝试其他关键词': 'Try other keywords', '暂无项目数据': 'No item data', '未命名': 'Unnamed', '成功加载{count}个诊疗项目': 'Loaded {count} treatment items', '未获取到诊疗项目数据': 'No treatment item data', '获取诊疗项目列表失败': 'Failed to get treatment item list', '请输入有效数量': 'Enter valid quantity', '未找到该项目信息': 'Item info not found', '折扣必须在0-100之间': 'Discount must be 0-100', '请完善必填项:': 'Please complete required fields:', '请至少添加一条套餐明细': 'Add at least one package detail', '请先确认所有正在编辑的行': 'Confirm all editing rows first', '请完善所有明细项目信息(项目名称和数量为必填项)': 'Complete all detail items (name and quantity required)', '保存失败,请检查表单数据': 'Save failed, check form data', '至': 'to', '卫生机构:': 'Healthcare Org:', '套餐级别:': 'Package Level:', '套餐类别:': 'Package Category:', '用户:': 'User:', '全院套餐': 'Hospital Package', '个人套餐': 'Personal Package', '总数:': 'Total:', '加载用户列表失败': 'Failed to load user list', '获取机构列表失败': 'Failed to get org list', '无法获取套餐ID,请刷新页面后重试': 'Cannot get package ID, refresh and retry', '无法获取套餐ID,删除失败': 'Cannot get package ID, delete failed', '编码': 'Code', '名称': 'Name', '类型': 'Type', '状态': 'Status', '操作': 'Action', '备注': 'Remark', '排序': 'Sort', '描述': 'Description', '科室': 'Dept', '部门': 'Department', '医生': 'Doctor', '护士': 'Nurse', '患者': 'Patient', '诊断': 'Diagnosis', '处方': 'Rx', '医嘱': 'Order', '费用': 'Fee', '金额': 'Amount', '数量': 'Qty', '单价': 'Price', '合计': 'Total', '开始时间': 'Start Time', '结束时间': 'End Time', '门诊': 'OPD', '住院': 'IPD', '急诊': 'Emergency', '手术': 'Surgery', '护理': 'Nursing', '检验': 'Lab', '检查': 'Inspection', '病历': 'EMR', '病案': 'Case', '药品': 'Drug', '耗材': 'Consumable', '报告': 'Report', '统计': 'Statistics', '报表': 'Report', '质量': 'Quality', '指标': 'Indicator', '管理': 'Management', '设置': 'Settings', '配置': 'Config', '参数': 'Parameter', '维护': 'Maintenance', '目录': 'Catalog', '对照': 'Mapping', '追溯': 'Traceability', '预警': 'Alert', '监测': 'Monitoring', '执行': 'Execution', '追踪': 'Tracking', '评估': 'Assessment', '筛查': 'Screening', '审批': 'Approval', '审核': 'Review', '闭环': 'Closed Loop', '组套': 'Template', '增强': 'Enhanced', '收费': 'Charge', '退费': 'Refund', '结算': 'Settlement', '挂号': 'Registration', '预约': 'Appointment', '发药': 'Dispensing', '退药': 'Drug Return', '入库': 'Inbound', '出库': 'Outbound', '盘点': 'Stocktaking', '调拨': 'Transfer', }; function translateDeep(obj) { let count = 0; for (const k in obj) { if (typeof obj[k] === 'object' && obj[k] !== null) { count += translateDeep(obj[k]); } else if (typeof obj[k] === 'string' && /[\u4e00-\u9fff]/.test(obj[k])) { if (direct[obj[k]]) { obj[k] = direct[obj[k]]; count++; } else { let result = obj[k]; const sorted = Object.entries(direct).sort((a, b) => b[0].length - a[0].length); for (const [zh, en] of sorted) { const escaped = zh.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); result = result.replace(new RegExp(escaped, 'g'), en); } result = result.replace(/\s+/g, ' ').trim(); if (result !== obj[k] && !/[\u4e00-\u9fff]/.test(result)) { obj[k] = result; count++; } } } } return count; } const c = translateDeep(enUS); fs.writeFileSync(path.join(__dirname, '..', 'src', 'i18n', 'locales', 'enUS.json'), JSON.stringify(enUS, null, 2), 'utf8'); console.log('Translated:', c); function countChinese(obj) { let c = 0; for (const k in obj) { if (typeof obj[k] === 'object' && obj[k] !== null) c += countChinese(obj[k]); else if (typeof obj[k] === 'string' && /[\u4e00-\u9fff]/.test(obj[k])) c++; } return c; } console.log('Remaining Chinese in enUS:', countChinese(enUS));