Files
web-Iot/tmp_gen.cjs
2026-08-14 09:02:52 +08:00

564 lines
54 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Generaate missing locale keys (best-effort) for zh + en.
const fs = require('fs');
const path = require('path');
const root = 'src';
const exts = ['.tsx', '.ts', '.jsx', '.js'];
const excludeDirs = ['locales', 'node_modules', 'i18n'];
function walk(dir) {
const out = [];
for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
const full = path.join(dir, e.name);
if (e.isDirectory()) { if (!excludeDirs.includes(e.name)) out.push(...walk(full)); }
else if (exts.includes(path.extname(e.name))) out.push(full);
}
return out;
}
function load(p) {
let s = fs.readFileSync(p, 'utf8');
s = s.replace(/const\s+\w+\s*=\s*\{/, 'var obj = {');
s = s.replace(/export\s+default\s+\w+;?/, '');
s = s.replace(/\bconst\b/g, 'var');
return new Function(s + '\nreturn obj;')();
}
function flat(o, p, r) {
p = p || ''; r = r || {};
for (const k of Object.keys(o)) {
const key = p ? p + '.' + k : k;
if (o[k] && typeof o[k] === 'object' && !Array.isArray(o[k])) flat(o[k], key, r);
else r[key] = o[k];
}
return r;
}
function unflat() { return {}; }
const zhObj = load('src/locales/zh/index.ts');
const enObj = load('src/locales/en/index.ts');
const zhMap = flat(zhObj);
const enMap = flat(enObj);
// ---- token dictionaries ----
// camelCase token -> Chinese (for inferring zh of missing keys)
const tokZh = {
tl: '时间线', timeline: '时间线', mock: '模拟', device: '设备', devices: '设备',
sensor: '传感器', camera: '摄像头', station: '基站', battery: '电量', temp: '温度', temperature: '温度',
uav: '无人机', drone: '无人机', mower: '割草机', robot: '机器人', video: '视频',
status: '状态', task: '任务', tasks: '任务', complete: '完成', completed: '已完成', completing: '完成中',
start: '开始', end: '结束', startcleaning: '开始清洗', startinspection: '开始巡检', startmowing: '开始除草',
standby: '待机', ready: '就绪', return: '返航', supply: '补给', finish: '完成', inspection: '巡检', inspect: '巡检',
mow: '除草', maintenance: '维护', repair: '维修', clean: '清洗', cleaning: '清洗',
msg: '消息', message: '消息', no: '编号', alarm: '告警', rule: '规则', rules: '规则',
param: '参数', params: '参数', left: '左', right: '右', forward: '前', backward: '后', gain: '增益',
speed: '速度', chip: '芯片', uid: 'UID', sign: '标识', route: '路线', routes: '路线', path: '路径', point: '点', points: '点',
indoor: '室内', outdoor: '室外', wide: '广角', zoom: '变焦', lens: '镜头', ir: '红外', switch: '切换',
fail: '失败', failed: '失败', success: '成功', fetch: '获取', save: '保存', create: '创建', created: '已创建',
generate: '生成', export: '导出', import: '导入', download: '下载', upload: '上传', search: '搜索', reset: '重置',
confirm: '确认', cancel: '取消', view: '查看', select: '选择', selected: '已选', name: '名称', type: '类型',
time: '时间', date: '日期', total: '共', count: '数量', today: '今日', yesterday: '昨日',
week: '周', month: '月', day: '日', year: '年', online: '在线', offline: '离线',
error: '错误', errors: '错误', warning: '警告', info: '提示', critical: '严重', important: '重要', general: '一般',
pending: '待处理', closed: '已关闭', handled: '已处理', restored: '已恢复', ignored: '已忽略', falsealarm: '误报',
level: '级别', source: '来源', result: '结果', trend: '趋势', distribution: '分布', statistics: '统计',
subscription: '订阅', history: '历史', realtime: '实时', overview: '概览', analysis: '分析', diagnosis: '诊断',
defect: '缺陷', thermal: '热成像', component: '组件', string: '组串', inverter: '逆变器', efficiency: '效率',
risk: '风险', confidence: '置信度', location: '位置', suggestion: '建议', plan: '计划', execute: '执行',
mowing: '除草', inspect: '巡检', work: '工作', order: '工单', user: '用户', role: '角色', roles: '角色',
permission: '权限', menu: '菜单', organization: '组织', notice: '通知', data: '数据', safety: '安全',
access: '接入', protocol: '协议', longitude: '经度', latitude: '纬度', timezone: '时区', capacity: '容量',
address: '地址', leader: '负责人', contact: '联系', icon: '图标', sort: '排序', parent: '上级',
directory: '目录', button: '按钮', theme: '主题', light: '浅色', dark: '深色', language: '语言', version: '版本',
serial: '序列', verify: '验证', bind: '绑定', code: '码', list: '列表', refresh: '刷新', delete: '删除',
edit: '编辑', add: '新增', update: '更新', enable: '启用', disable: '停用', normal: '正常', abnormal: '异常',
loading: '加载中', generating: '生成中', processing: '处理中', exported: '已导出', uploadfile: '上传文件',
uploadimage: '上传图片', startanalysis: '开始分析', analysisresult: '分析结果', detected: '检测到',
noresult: '无结果', none: '无', unknown: '未知', invalid: '无效', gps: 'GPS', dgps: 'DGPS', rtk: 'RTK',
fixed: '固定', float: '浮点', config: '配置', configured: '已配置', connection: '连接', connect: '连接',
request: '请求', permissionreq: '权限请求', recover: '恢复', resume: '恢复', pause: '暂停', stopped: '停用',
working: '作业中', workingrobots: '作业中机器人', stat: '统计', alert: '告警', alerts: '告警',
deviceenvironment: '设备环境', currenttemp: '当前温度', humidity: '环境湿度', positionaccuracy: '定位精度',
networklatency: '网络延迟', waterrisk: '积水风险', preset: '预设', searchplaceholder: '搜索', unnamed: '未命名',
deleteroute: '删除航线', executeroute: '执行航线', taskboard: '任务看板', pendingtask: '待执行任务',
executing: '执行中', executingellipsis: '执行中', pausedtask: '已暂停任务', executeroutetask: '执行路线任务',
routeinfo: '路线信息', routename: '路线名称', selectexecdevice: '选择执行设备', panorama: '全景',
wdsun: '日', wdmon: '一', wtue: '二', wdwend: '三', wdthu: '四', wdfri: '五', wdsat: '六',
deviceserial: '设备编号', fillrequired: '请填写', selectexectime: '选择执行时间', taskcreated: '任务创建成功',
createfailed: '创建失败', createtaskerror: '创建任务异常', newtask: '新建任务', inputtaskname: '请输入任务名称',
executeroutelabel: '执行路线', selectroute: '选择路线', repeatplan: '重复计划', execdate: '执行日期',
selectexecdate: '选择执行日期', exectime: '执行时间', savetask: '保存任务', devicecontrol: '设备控制',
realtimevideo: '实时视频', takeoff: '起飞', land: '降落', returnhome: '返航', emergencystop: '紧急停止',
rotateleft: '左旋', rotateright: '右旋', gimbalpitch: '云台俯仰', gimbályaw: '云台偏航', zoomin: '放大', zoomout: '缩小',
wayline: '航线', waylinetask: '航线任务', robottask: '机器人任务', devicename: '设备名称', devicetype: '设备类型',
devicemodel: '设备型号', onlinestatus: '在线状态', lastheartbeat: '最后心跳', batterylevel: '电量',
signalstrength: '信号强度', control: '控制', uploadwayline: '上传航线', downloadwayline: '下载航线',
executetask: '执行任务', taskname: '任务名称', taskstatus: '任务状态', flighttime: '飞行时间', altitude: '高度',
distance: '距离', starttime: '开始时间', endtime: '结束时间', drone: '无人机', camera2: '摄像头',
creator: '创建者', executionplan: '执行计划', executioncycle: '执行周期', statusnew: '新建',
statuspaused: '暂停中', statusfinished: '执行成功', statusfailed: '执行失败', confirmdeletetask: '确定要删除该任务吗',
confirmcanceltask: '确定要取消该任务吗', confirmpausetask: '确定要暂停该任务吗', clearrealtime: '清空实时',
cleartrack: '清空实时轨迹', clearplanroute: '清空规划航线', controlmode: '控制模式', localmode: '本地',
remotemode: '远程', heading: '航向', initialized: '已初始化', uninitialized: '未初始化', taskpool: '任务池',
taskexecutionplan: '任务执行计划', taskhistory: '历史任务', createtask: '创建任务', startdate: '开始日期',
enddate: '结束日期', allrobots: '全部机器人', operationsuggestion: '作业编排建议', good: '良好',
devicelocation: '设备定位', devicevideo: '设备视频', robotpanorama: '机器人全景',
dayplan: '按天计划', weekplan: '按周计划', monthplan: '按月计划', daysuffix: '日', unknownplan: '未知计划',
mon: '周一', tue: '周二', wed: '周三', thu: '周四', fri: '周五', sat: '周六', sun: '周日',
vs: '较', unit: '单位', unitplan: '条', unitrobot: '台', unittimes: '次', completionrate: '完成率',
alertscount: '告警数量', costsaving: '节省成本', monthlyaccumulated: '本月累计', daily: '日', target: '目标',
installedcapacity: '装机容量', generationtrend: '发电趋势', irradiance: '辐照度', actual: '实际', predict: '预测',
invertterefficiency: '逆变器效率', maintenanceprogress: '运维进度', stationoverview: '电站概览',
recentinspection: '近期巡检', aianalysissummary: 'AI分析摘要', realtimealerts: '实时告警',
deviceonlinestatus: '设备在线状态', weatherinfo: '天气信息', cleaningsuggestion: '清洗建议',
cleanordergenerated: '清洗工单生成成功', pleaseelectstation: '请选择电站', generatecleanorder: '生成清洗工单',
kwh: 'kWh', kwp: 'kWp', undermaintenance: '维护中', abnormal2: '异常', normal2: '正常',
moduleinfo: '模块信息', heat: '热', hotspot: '热斑', component0: '组件', datapoint: '数据点', datapoints: '数据点',
exportfilename: '导出文件', processing2: '处理中', loadfailed: '加载失败', loaddetailfailed: '加载详情失败',
requesterror: '请求错误', handlesuccess: '处理成功', handlefail: '处理失败', alarmno: '告警编号',
devicedatas: '设备数据', working2: '作业中', mockdevice: '模拟设备', mocksensor: '模拟传感器',
mocktemp: '模拟温度', mocktempexceed: '模拟温度超限', mockbattery: '模拟电量', mockbatterylow: '模拟电量低',
mockvideosignal: '模拟视频信号', mockvideosignallost: '模拟视频信号丢失', mockrule: '模拟规则',
mocksub: '模拟订阅', mocksubops: '模拟运维', mocksubadmin: '模拟管理员', mocksubdevice: '模拟设备',
sms: '短信', email: '邮件', apppush: 'App推送', subscriptionname: '订阅名称', notifychannel: '通知渠道',
devicesensor: '设备传感器', devicebattery: '设备电量', alarmlevel: '告警级别', alarmtype: '告警类型',
alarmsource: '告警来源', alarmtime: '告警时间', handlestatus: '处理状态', handleresult: '处理结果',
handletime: '处理时间', handler: '处理人', handleremark: '处理备注', handle: '处理', closealert: '关闭告警',
confirmalert: '确认告警', exportalert: '导出告警', critical2: '严重', important2: '重要', general2: '一般',
info2: '提示', reopen: '重新打开', sevenDayTrend: '近7天趋势', sevenDayTrend2: '近7天趋势',
criticalAlert: '严重告警', importantAlert: '重要告警', generalAlert: '一般告警', infoAlert: '提示告警',
daySuffix2: '日', daySuffix3: '日', exported2: '已导出', loadDetailFailed2: '加载详情失败',
requestError2: '请求错误', handleSuccess2: '处理成功', handleFail2: '处理失败', alarmNo2: '告警编号',
locationUnknown: '未知位置', none2: '无', good2: '良好',
taskCompletedMsg: '任务完成消息', confirmDeleteTitle: '确认删除', confirmDeleteContent: '确认删除内容',
confirmDeleteOk: '确认删除', routeDeletedSuccess: '路线删除成功', deleteFailRetry: '删除失败重试',
deleteFailNetwork: '删除失败网络', taskListRefreshFail: '任务列表刷新失败', routeNoPathPoints: '路线无路径点',
waylineFallback: '航线兜底', waylineListFetchFail: '航线列表获取失败', fillRequiredFields: '请填写必填项',
createTaskSuccess: '创建任务成功', createTaskFail: '创建任务失败', createTaskException: '创建任务异常',
trendYesterday: '较昨日', trendFlat: '持平', taskListFetchFail: '任务列表获取失败',
fetchParamFail: '参数获取失败', paramSaveSuccess: '参数保存成功', paramSaveFail: '参数保存失败',
paramSaveException: '参数保存异常', paramLeftForwardGain: '左前增益', paramLeftBackwardGain: '左后增益',
paramRightForwardGain: '右前增益', paramRightBackwardGain: '右后增益', paramRunSpeed: '运行速度',
paramChipUidSign: '芯片UID标识', timelineStatusStart: '开始', timelineStatusReady: '就绪',
timelineStatusReturn: '返航', timelineStatusComplete: '完成', tlStartCleaning: '开始清洗',
tlStartInspection: '开始巡检', tlStartMowing: '开始除草', tlStandbyReady: '待机就绪',
tlReturnSupply: '返航补给', tlFinishInspection: '完成巡检', tlFinishMowing: '完成除草',
stationCameraNotFound: '基站摄像头未找到', switchStationCameraFail: '切换基站摄像头失败',
switchDroneLensFail: '切换无人机镜头失败', wideLens: '广角镜头', zoomLens: '变焦镜头', irLens: '红外镜头',
rainNo: '无雨', pointMarked: '已标记点', minThreePoints: '至少三个点', pathPlanDone: '路径规划完成',
pathPlanFailRetry: '路径规划失败重试', completePathFirst: '请先完成路径', inputPlotName: '输入区域名称',
connectionFailed: '连接失败', permissionRequestTitle: '权限请求', requestControlPermission: '请求控制权限',
selectNoDrone: '未选择无人机', commandIssued: '指令已下发', commandFailed: '指令下发失败',
videoConfigured: '视频已配置', noVideo: '无视频', working3: '作业中', online2: '在线', offline2: '离线',
// generic fallbacks
page: '页面', setting: '设置', settings: '设置', manage: '管理', management: '管理', center: '中心',
report2: '报表', log2: '日志', profile: '个人中心', login: '登录', logout: '退出', station2: '电站',
monitor2: '监控', ai: 'AI', optimization: '优化', task2: '任务', order2: '工单', alert2: '告警',
device2: '设备', system2: '系统', user2: '用户', role2: '角色',
};
// Comprehensive English-token -> Chinese lexicon (covers tokens from missing keys)
const lex = {
a01: '', ab: '', abnormal: '异常', account: '账号', accumulated: '累计', action: '操作', activated: '已激活', active: '启用',
add: '新增', added: '已添加', additional: '额外', admin: '管理员', advice: '建议', after: '之后', ai: 'AI', airport: '机场', airspace: '空域',
alarm: '告警', alert: '告警', alerts: '告警', algo: '算法', algorithm: '算法', alias: '别名', all: '全部', altitude: '高度', ambient: '环境',
analysis: '分析', and: '与', angle: '角度', api: 'API', app: '应用', approval: '审批', area: '区域', arm: '布防', array: '阵列', assign: '分配',
assigned: '已分配', async: '异步', auto: '自动', average: '平均', avg: '平均', avoid: '避开', back: '返回', backward: '后退', basic: '基础',
batch: '批量', battery: '电量', belonging: '所属', block: '区块', board: '看板', bound: '边界', bow: '船首', breakpoint: '断点', btn: '按钮',
by: '按', calendar: '日历', call: '呼叫', camera: '摄像头', cameras: '摄像头', can: '可', cancel: '取消', canceled: '已取消', cancelled: '已取消',
capacity: '容量', cargo: '货物', cause: '原因', center: '中心', change: '变更', channel: '渠道', channels: '渠道', charge: '充电', chassis: '底盘',
check: '检查', chip: '芯片', choose: '选择', clean: '清洗', cleaning: '清洗', clear: '清空', cleared: '已清空', click: '点击', climb: '爬升',
closed: '已关闭', closure: '闭合', code: '码', col: '列', colon: '冒号', comm: '通信', command: '指令', communicating: '通信中', communication: '通信',
compare: '对比', complete: '完成', completed: '已完成', completion: '完成', component: '组件', condition: '条件', conditions: '条件',
config: '配置', configured: '已配置', confirm: '确认', connected: '已连接', connection: '连接', contact: '联系', content: '内容', continuous: '连续',
control: '控制', controlling: '控制中', coordinated: '协同', count: '数量', cover: '覆盖', coverage: '覆盖', create: '创建', created: '已创建',
critical: '严重', cum: '累计', current: '当前', curve: '曲线', custom: '自定义', cycle: '周期', daily: '每日', data: '数据', date: '日期',
day: '日', days: '天', debug: '调试', default: '默认', del: '删除', delete: '删除', deleted: '已删除', demo: '演示', desc: '描述', description: '描述',
detail: '详情', details: '详情', detect: '检测', detected: '已检测', detection: '检测', deviation: '偏差', device: '设备', devices: '设备',
dgps: 'DGPS', diagnosis: '诊断', diff: '差异', ding: '钉钉', directory: '目录', dispatch: '派单', dispatchable: '可派单', display: '显示',
distribution: '分布', dock: '停靠', dome: '穹顶', done: '已完成', down: '下降', drag: '拖拽', drive: '驱动', drone: '无人机', drones: '无人机',
dual: '双', duty: '值班', edit: '编辑', effect: '效果', email: '邮件', emergency: '紧急', empty: '空', enabled: '已启用', end: '结束', ended: '已结束',
energy: '能量', engineer: '工程师', env: '环境', equipment: '设备', error: '错误', estimated: '预计', event: '事件', events: '事件', exceed: '超限',
excel: 'Excel', exception: '异常', exec: '执行', execute: '执行', executed: '已执行', executing: '执行中', execution: '执行', expired: '已过期',
export: '导出', fail: '失败', failed: '失败', failure: '失败', fallback: '兜底', fan: '风扇', fence: '围栏', fetch: '获取', fields: '字段', file: '文件',
fill: '填写', finish: '完成', fire: '火警', firmware: '固件', first: '先', fixed: '固定', flag: '标志', flat: '持平', flight: '飞行', float: '浮点',
fly: '飞行', forbidden: '禁止', formats: '格式', forward: '前进', found: '找到', frequency: '频率', fri: '周五', from: '从', front: '前', fuel: '燃料',
full: '全', gain: '增益', gantt: '甘特', gate: '闸门', ge: '地理', gear: '档位', general: '一般', generate: '生成', generated: '已生成',
generating: '生成中', generation: '发电', generator: '发电机', geojson: 'GeoJSON', get: '获取', gnss: 'GNSS', good: '良好', gps: 'GPS', gr: '电网',
grid: '电网', group: '分组', handle: '处理', handled: '已处理', handler: '处理人', hangar: '机库', has: '有', header: '表头', heading: '航向',
health: '健康', heatmap: '热力图', height: '高度', high: '高', hint: '提示', history: '历史', home: '首页', home2: '首页', horizontal: '水平',
hotspot: '热斑', hour: '小时', id: 'ID', image: '图片', immediate: '立即', important: '重要', improvement: '提升', in: '内', incident: '事件',
indoor: '室内', inference: '推理', info: '提示', initialized: '已初始化', input: '输入', inspect: '巡检', inspection: '巡检', integrated: '集成',
intelligent: '智能', interactive: '交互', intercom: '对讲', interval: '间隔', intrusion: '入侵', invalid: '无效', inverter: '逆变器', ir: '红外',
irradiance: '辐照度', irreversible: '不可逆', issue: '问题', issued: '已下发', item: '项', items: '项', json: 'JSON', key: '键', keyword: '关键词',
knife: '刀', label: '标签', landing: '降落', language: '语言', last: '最后', layout: '布局', left: '左', legend: '图例', length: '长度', lens: '镜头',
level: '级别', levels: '级别', lift: '抬升', limit: '限制', link: '链接', list: '列表', live: '直播', load: '加载', loaded: '已加载', local: '本地',
location: '位置', log: '日志', login: '登录', logs: '日志', loitering: '徘徊', lost: '丢失', low: '低', machine: '机器', main: '主', manage: '管理',
management: '管理', manager: '管理员', manual: '手动', map: '地图', mark: '标记', marked: '已标记', marks: '标记', mask: '掩码', members: '成员',
menu: '菜单', meteorology: '气象', mid: '中间', mileage: '里程', min: '最小', mock: '模拟', mode: '模式', model: '模型', models: '模型',
modified: '已修改', modify: '修改', mon: '周一', monitor: '监控', month: '月', monthly: '每月', more: '更多', motion: '运动', motor: '电机', mount: '挂载',
mow: '除草', mowing: '除草', msg: '消息', name: '名称', near: '附近', needed: '需要', network: '网络', new: '新', nickname: '昵称', night: '夜间',
no: '无', none: '无', normal: '正常', normally: '正常', north: '北', not: '未', notification: '通知', notify: '通知', now: '现在', number: '编号',
object: '对象', obstacle: '障碍', obtained: '已获取', occur: '发生', of: '的', offline: '离线', ok: '确定', old: '旧', om: '运维', on: '开', one: '一',
online: '在线', only: '仅', open: '打开', operate: '操作', operation: '操作', ops: '运维', optimal: '最优', optimization: '优化', optimize: '优化',
optional: '可选', or: '或', orchestration: '编排', order: '工单', orders: '工单', org: '组织', original: '原始', other: '其他', out: '外', outdoor: '室外',
over: '超过', overlay: '叠加', overview: '概览', page: '页面', panel: '面板', panorama: '全景', param: '参数', params: '参数', password: '密码',
path: '路径', pause: '暂停', paused: '已暂停', pdf: 'PDF', pending: '待处理', per: '每', percent: '百分比', perimeter: '周长', perm: '权限',
permission: '权限', person: '人员', personnel: '人员', phase: '相', phone: '电话', picture: '图片', pitch: '俯仰', pixels: '像素', placeholder: '占位',
plan: '计划', plane: '飞机', planned: '已计划', planning: '规划', platform: '平台', play: '播放', please: '请', plot: '区域', point: '点', points: '点',
polarity: '极性', port: '端口', position: '位置', positioning: '定位', power: '功率', pr: '光伏', precip: '降水', precision: '精度', predicted: '预测',
preparing: '准备中', preset: '预设', preview: '预览', priority: '优先级', probe: '探测', processing: '处理中', product: '产品', progress: '进度',
property: '属性', proportion: '比例', protect: '保护', ptz: '云台', pump: '泵', push: '推送', quality: '质量', quantity: '数量', rain: '雨',
rainfall: '降雨', range: '范围', rate: '率', rated: '额定', ratio: '比例', rbt: '机器人', read: '读取', ready: '就绪', realtime: '实时', rear: '后',
recent: '近期', recharge: '充电', recheck: '复检', recognition: '识别', recommended: '建议', record: '记录', recording: '录制', records: '记录',
recurring: '重复', refresh: '刷新', registry: '注册', related: '关联', relay: '中继', remark: '备注', remote: '远程', repeat: '重复', repellent: '驱避',
replay: '回放', report: '报表', reports: '报表', request: '请求', requesting: '请求中', required: '必填', reset: '重置', restored: '已恢复', result: '结果',
resume: '恢复', resumed: '已恢复', retry: '重试', return: '返航', returned: '已返航', revenue: '收益', reverse: '反向', right: '右', risk: '风险',
robot: '机器人', role: '角色', roles: '角色', room: '房间', root: '根', route: '路线', routes: '路线', rt: '实时', rth: '返航', rtk: 'RTK', rule: '规则',
run: '运行', running: '运行中', safe: '安全', sat: '卫星', satellite: '卫星', satellites: '卫星', save: '保存', saved: '已保存', saving: '节省',
schedule: '计划', scheduled: '已计划', screen: '屏幕', search: '搜索', seg: '分割', segment: '段', segmentation: '分割', select: '选择', selected: '已选',
send: '发送', sensor: '传感器', sent: '已发送', server: '服务器', services: '服务', servo: '伺服', setting: '设置', settings: '设置', seven: '七',
short: '短', show: '显示', sign: '标识', signal: '信号', single: '单', site: '站点', situation: '态势', size: '大小', smart: '智能', smoke: '烟', sms: '短信',
sn: '序列号', snapshots: '快照', sort: '排序', source: '来源', south: '南', span: '跨度', speed: '速度', standby: '待机', start: '开始', started: '已开始',
station: '电站', statistics: '统计', status: '状态', statuses: '状态', stop: '停止', stopped: '已停止', storage: '存储', store: '存储', stream: '流',
streams: '流', string: '组串', sub: '订阅', submit: '提交', subscribers: '订阅者', subscription: '订阅', subtitle: '字幕', success: '成功', suffix: '后缀',
suggest: '建议', suggested: '建议', suggestion: '建议', suitability: '适宜性', suitable: '适宜', summary: '摘要', sun: '太阳', super: '超级',
supervisor: '主管', supply: '补给', supported: '支持', supports: '支持', suspect: '疑似', suspend: '暂停', suspended: '已暂停', swap: '切换',
switch: '切换', symbol: '符号', sync: '同步', system: '系统', tai: '台', talk: '通话', task: '任务', tasks: '任务', temp: '温度', temperature: '温度',
template: '模板', terminated: '已终止', theoretical: '理论', thermal: '热成像', three: '三', threshold: '阈值', throttle: '油门', thu: '周四',
tiao: '条', time: '时间', timed: '定时', timeline: '时间线', timeout: '超时', timestamp: '时间戳', title: '标题', tl: '时间线', to: '到', today: '今日',
top: '顶部', topology: '拓扑', total: '总', tracking: '跟踪', trend: '趋势', trigger: '触发', triggered: '已触发', tue: '周二', turn: '转向', two: '二',
type: '类型', types: '类型', tyre: '轮胎', uav: '无人机', uid: 'UID', unit: '单位', unknown: '未知', up: '上升', update: '更新', upgrading: '升级中',
upload: '上传', usage: '用量', use4g: '4G', user: '用户', users: '用户', uuid: 'UUID', value: '值', version: '版本', vertical: '垂直', video: '视频',
view: '查看', visual: '可视', voltage: '电压', vs: '较', wait: '等待', waiting: '等待中', walk: '行走', wall: '墙', wash: '清洗', way: '路径',
wayline: '航线', waypoint: '航点', waypoints: '航点', weather: '天气', web: '网页', wed: '周三', weed: '杂草', week: '周', weekday: '工作日',
weekly: '每周', west: '西', wheel: '轮', which: '哪个', wide: '广角', width: '宽度', wifi: 'WiFi', wind: '风', window: '窗口', wing: '机翼', with: '带',
work: '工作', working: '作业中', workspace: '工作区', yes: '是', yesterday: '昨日', zero: '零', zone: '区域', zoom: '缩放',
};
const NOUNS_WITH_NO = new Set(['alarm', 'device', 'order', 'task', 'route', 'serial', 'station', 'work', 'user', 'role', 'id', 'sn', 'uav', 'drone', 'robot']);
// Chinese phrase/char -> English (for translating hardcoded chinese strings)
const zhEn = {
'保存': 'Save', '保存修改': 'Save Changes', '取消': 'Cancel', '确认': 'Confirm', '确定': 'OK', '删除': 'Delete',
'编辑': 'Edit', '新增': 'Add', '查询': 'Search', '搜索': 'Search', '重置': 'Reset', '提交': 'Submit', '关闭': 'Close',
'返回': 'Back', '导出': 'Export', '导入': 'Import', '下载': 'Download', '上传': 'Upload', '刷新': 'Refresh',
'查看': 'View', '查看全部': 'View All', '查看详情': 'View Details', '操作': 'Action', '状态': 'Status', '类型': 'Type',
'名称': 'Name', '描述': 'Description', '备注': 'Remark', '时间': 'Time', '日期': 'Date', '创建时间': 'Created At',
'更新时间': 'Updated At', '启用': 'Enable', '停用': 'Disable', '正常': 'Normal', '加载中': 'Loading',
'加载中...': 'Loading...', '暂无数据': 'No Data', '成功': 'Success', '失败': 'Failed', '删除成功': 'Deleted',
'删除失败': 'Delete failed', '编辑成功': 'Updated', '编辑失败': 'Update failed', '新增成功': 'Added',
'新增失败': 'Add failed', '保存成功': 'Saved', '保存失败': 'Save failed', '确定删除?': 'Are you sure to delete?',
'请选择': 'Please select', '请输入': 'Please enter', '全部': 'All', '是': 'Yes', '否': 'No', '男': 'Male', '女': 'Female',
'未知': 'Unknown', '未设置': 'Not set', '共': 'Total', '条': 'Items', '全选': 'Select all', '批量删除': 'Batch delete',
'已选': 'Selected', '更多': 'More', '展开': 'Expand', '收起': 'Collapse', '已完成': 'Completed',
'星期一': 'Monday', '星期二': 'Tuesday', '星期三': 'Wednesday', '星期四': 'Thursday', '星期五': 'Friday',
'星期六': 'Saturday', '星期日': 'Sunday',
'光伏机器人环境感知服务平台': 'PV Plant Intelligent Monitoring & O&M System',
'请选择电站': 'Please select a station', '全屏': 'Full Screen', '退出全屏': 'Exit Full Screen', '退出登录': 'Logout',
'电站地址': 'Station Address', '电站类型': 'Station Type', '运行状态': 'Running Status', '正常运行': 'Normal Running',
'地面电站': 'Ground Station', '1MW 光伏电站': '1MW PV Plant', '河南省林州': 'Linzhou, Henan',
'模块开发中': 'Module Developing',
'请输入账号': 'Enter account', '请输入密码': 'Enter password', '账号': 'Account', '密码': 'Password',
'登录': 'Login', '登录成功': 'Login success', '登录失败': 'Login failed', '欢迎': 'Welcome',
'用户协议': 'User Agreement', '隐私政策': 'Privacy Policy', '我已阅读并同意': 'I have read and agree',
'请阅读并同意用户协议': 'Please read and agree to the user agreement',
'总览首页': 'Overview', '设备管理': 'Device Management', '设备总览': 'Device Overview', '设备状态': 'Device Status',
'航线任务': 'Wayline Task', '机器人任务': 'Robot Task', '在线视频': 'Online Video', '实时监控': 'Real-time Monitor',
'告警中心': 'Alert Center', 'AI诊断分析': 'AI Diagnosis', '清洗优化': 'Cleaning Optimization', '工单任务': 'Work Order',
'报表中心': 'Report Center', '系统设置': 'System Settings', '日志管理': 'Log Management', '视频管理': 'Video Management',
'视频下载': 'Video Download', '用户管理': 'User Management', '角色管理': 'Role Management', '菜单管理': 'Menu Management',
'组织管理': 'Organization Management', '场站管理': 'Station Management', '通知策略页面': 'Notice Policy',
'数据与安全页面': 'Data & Safety', '个人中心': 'Profile', '请选择菜单': 'Please select a menu',
'当前功率': 'Current Power', '系统可用率': 'System Availability', '告警数量': 'Alert Count',
'节省运维成本': 'O&M Cost Saving', '日目标': 'Daily Target', '装机容量': 'Installed Capacity', '昨日': 'Yesterday',
'未处理': 'Unhandled', '已关闭': 'Closed', '本月累计节省': 'Monthly Accumulated Saving', '发电趋势': 'Generation Trend',
'辐照度与温度趋势': 'Irradiance & Temperature Trend', '逆变器效率对比': 'Inverter Efficiency', '运维任务进度': 'O&M Progress',
'电站概览': 'Station Overview', '近期巡检记录': 'Recent Inspection', 'AI 分析摘要': 'AI Analysis Summary',
'实时告警': 'Real-time Alerts', '设备在线状态': 'Device Online Status', '天气信息': 'Weather Info',
'清洗建议': 'Cleaning Suggestion', '实际': 'Actual', '预测': 'Predicted', '辐照度': 'Irradiance', '温度': 'Temperature',
'汇流箱': 'Combiner Box', '逆变器': 'Inverter', '巡检机器人': 'Inspection Robot', '异常': 'Abnormal', '维护中': 'Under Maintenance',
'生成清洗工单': 'Generate Cleaning Order', '请先选择电站': 'Please select station first', '清洗工单生成成功': 'Cleaning order created',
'kWh': 'kWh', 'kWp': 'kWp',
'设备名称': 'Device Name', '设备类型': 'Device Type', '设备型号': 'Device Model', '在线状态': 'Online Status',
'最后心跳': 'Last Heartbeat', '电量': 'Battery', '信号强度': 'Signal Strength', '位置': 'Location', '控制': 'Control',
'上传航线': 'Upload Wayline', '下载航线': 'Download Wayline', '执行任务': 'Execute Task', '任务名称': 'Task Name',
'任务状态': 'Task Status', '飞行时间': 'Flight Time', '高度': 'Altitude', '速度': 'Speed', '距离': 'Distance',
'开始时间': 'Start Time', '结束时间': 'End Time', '无人机': 'UAV', '割草机': 'Mower', '机器人': 'Robot', '摄像头': 'Camera',
'在线': 'Online', '离线': 'Offline', '待机': 'Standby', '运行中': 'Running', '充电中': 'Charging', '空闲': 'Idle',
'忙碌': 'Busy', '故障': 'Fault', '设备控制': 'Device Control', '实时视频': 'Real-time Video', '起飞': 'Takeoff',
'降落': 'Land', '返航': 'Return', '暂停': 'Pause', '继续': 'Resume', '紧急停止': 'Emergency Stop', '前进': 'Forward',
'后退': 'Backward', '左转': 'Turn Left', '右转': 'Turn Right', '上升': 'Up', '下降': 'Down', '左旋': 'Rotate Left',
'右旋': 'Rotate Right', '云台俯仰': 'Gimbal Pitch', '云台偏航': 'Gimbal Yaw', '放大': 'Zoom In', '缩小': 'Zoom Out',
'创建者': 'Creator', '执行计划': 'Execution Plan', '周日': 'Sun', '周一': 'Mon', '周二': 'Tue', '周三': 'Wed',
'周四': 'Thu', '周五': 'Fri', '周六': 'Sat', '执行周期': 'Execution Cycle', '新建': 'New', '暂停中': 'Paused',
'执行成功': 'Finished', '执行失败': 'Failed', '确定要删除该任务吗?': 'Delete this task?', '确定要取消该任务吗?': 'Cancel this task?',
'确定要暂停该任务吗?': 'Pause this task?', '恢复': 'Resume', '今日机器人计划': 'Today Robot Plan',
'进行中': 'In Progress', '作业中机器人': 'Working Robots', '台': 'Units', '作业中': 'Working', '累计作业里程': 'Total Distance',
'较昨日': 'vs Yesterday', '任务完成率': 'Task Completion Rate', '异常告警': 'Abnormal Alerts', '次': 'Times',
'在线率': 'Online Rate', '良好': 'Good', '机器人路径规划与监控': 'Robot Path Planning & Monitor',
'清空实时轨迹': 'Clear Track', '清空规划航线': 'Clear Plan', '控制模式:': 'Control Mode:', '本地': 'Local', '远程': 'Remote',
'航向:': 'Heading:', '已初始化': 'Initialized', '未初始化': 'Uninitialized', '定位:': 'Location:', '视频:': 'Video:',
'任务池': 'Task Pool', '任务执行计划': 'Task Execution Plan', '历史任务': 'Task History', '创建任务': 'Create Task',
'开始日期': 'Start Date', '结束日期': 'End Date', '全部机器人': 'All Robots', '作业编排建议': 'Operation Suggestion',
'开始除草任务': 'Start Mowing', '预计耗时 2 小时,当前进度 45%': 'ETA 2h, progress 45%', '预定巡检任务': 'Scheduled Inspection',
'待审批,建议执行区域 C': 'Pending, suggest area C', '设备例行维护': 'Routine Maintenance', '所有割草机返回充电站': 'All mowers return',
'智能生成今日计划': 'Auto-generate Today Plan', '设备环境与状态': 'Device Environment & Status', '当前温度': 'Current Temp',
'环境湿度': 'Humidity', '定位精度': 'Position Accuracy', 'RTK 固定': 'RTK Fixed', '网络延迟': 'Network Latency',
'注意:区域 B 存在积水风险,建议避开': 'Caution: area B flooding risk, avoid', '预设路线': 'Preset Routes',
'搜索...': 'Search...', '未命名路线': 'Unnamed Route', '删除航线': 'Delete Route', '执行航线': 'Execute Route',
'任务看板': 'Task Board', '待执行': 'Pending', '正在执行...': 'Executing...', '已暂停': 'Paused', '执行路线任务': 'Execute Route Task',
'执行': 'Execute', '路线信息': 'Route Info', '路线名称:': 'Route Name:', '请选择执行设备': 'Select Device', '机器人全景视频 - ': 'Robot Panorama - ',
'设备编号': 'Device No', '路线': 'Route', '请填写:': 'Please fill:', '请选择执行时间': 'Select Exec Time',
'任务创建成功': 'Task Created', '创建失败': 'Create Failed', '创建任务异常': 'Create Task Error', '新建任务': 'New Task',
'请输入任务名称': 'Enter Task Name', '执行路线': 'Execute Route', '选择路线': 'Select Route', '重复计划': 'Repeat Plan',
'天': 'Day', '周': 'Week', '月': 'Month', '执行日期': 'Exec Date', '选择执行日期': 'Select Exec Date', '执行时间': 'Exec Time',
'保存任务': 'Save Task',
'视频监控': 'Video Monitor', '实时监控': 'Real-time Monitor', '视频墙': 'Video Wall', '视频下载': 'Video Download',
'截图': 'Snapshot', '录制': 'Record', '播放': 'Play', '停止': 'Stop', '静音': 'Mute', '取消静音': 'Unmute', '直播': 'Live',
'回放': 'Replay', '选择设备': 'Select Device', '选择摄像头': 'Select Camera', '暂无视频': 'No Video', '视频加载中...': 'Loading video...',
'画质': 'Quality', '流畅': 'Fluency', '标清': 'SD', '高清': 'HD', '超清': 'FHD', '下载时间': 'Download Time',
'文件大小': 'File Size', '文件名': 'File Name', '下载状态': 'Download Status', '下载中': 'Downloading', '已下载': 'Downloaded',
'下载失败': 'Download Failed', '监控模式': 'Monitor Mode', '单画面': 'Single', '四画面': 'Quad', '九画面': 'Nine', '十六画面': 'Sixteen',
'告警概览': 'Alert Overview', '实时告警': 'Real-time Alert', '历史告警': 'Alert History', '告警规则': 'Alert Rules',
'告警统计': 'Alert Statistics', '告警订阅': 'Alert Subscription', '告警级别': 'Alert Level', '告警类型': 'Alert Type',
'告警来源': 'Alert Source', '告警时间': 'Alert Time', '处理状态': 'Handle Status', '处理结果': 'Handle Result',
'处理时间': 'Handle Time', '处理人': 'Handler', '处理备注': 'Handle Remark', '处理': 'Handle', '关闭告警': 'Close Alert',
'确认告警': 'Confirm Alert', '导出告警': 'Export Alert', '严重': 'Critical', '重要': 'Important', '一般': 'General',
'提示': 'Info', '待处理': 'Pending', '已处理': 'Handled', '已恢复': 'Restored', '已忽略': 'Ignored', '无法处理': 'Unable',
'厂家处理中': 'Manufacturer', '误报': 'False Alarm', '告警总数': 'Total Alerts', '今日告警': 'Today Alerts',
'未处理告警': 'Unhandled Alerts', '处理率': 'Handle Rate', '无人机故障': 'UAV Fault', '割草机故障': 'Mower Fault',
'其他设备故障': 'Other Device Fault', '服务器故障': 'Server Failure', '系统错误': 'System Error', '告警趋势': 'Alert Trend',
'告警分布': 'Alert Distribution',
'AI诊断分析': 'AI Diagnosis', '热成像分析': 'Thermal Analysis', '缺陷检测': 'Defect Detection', '温度分析': 'Temp Analysis',
'热斑检测': 'Hotspot Detection', '组件分析': 'Component Analysis', '组串分析': 'String Analysis', '逆变器分析': 'Inverter Analysis',
'上传图片': 'Upload Image', '开始分析': 'Start Analysis', '分析结果': 'Analysis Result', '缺陷类型': 'Defect Type',
'缺陷等级': 'Defect Level', '置信度': 'Confidence', '位置': 'Location', '处理建议': 'Suggestion', '异常温度': 'Abnormal Temp',
'最高温度': 'Max Temp', '平均温度': 'Avg Temp', '最低温度': 'Min Temp', '正常温度': 'Normal Temp', '热斑区域': 'Hotspot Area',
'风险等级': 'Risk Level', '高风险': 'High Risk', '中风险': 'Medium Risk', '低风险': 'Low Risk', '无风险': 'No Risk',
'检测到缺陷': 'Defects Detected', '未检测到缺陷': 'No Defect', '分析历史': 'Analysis History', '重新分析': 'Re-analyze',
'清洗优化': 'Cleaning Optimization', '清洗任务': 'Cleaning Task', '清洗建议': 'Cleaning Suggestion', '清洗计划': 'Cleaning Plan',
'清洗记录': 'Cleaning Record', '清洗效果': 'Cleaning Effect', '清洗前效率': 'Eff Before', '清洗后效率': 'Eff After',
'效率提升': 'Eff Improvement', '预计收益': 'Est Revenue', '清洗成本': 'Clean Cost', '净收益': 'Net Revenue', '优先级': 'Priority',
'建议日期': 'Suggest Date', '清洗区域': 'Area', '清洗方式': 'Method', '人工清洗': 'Manual', '机器人清洗': 'Robot',
'水清洗': 'Water', '干清洗': 'Dry', '状态': 'Status', '待清洗': 'Pending', '清洗中': 'Cleaning', '已清洗': 'Cleaned',
'已取消': 'Cancelled', '生成工单': 'Generate Order', '污损率': 'Soiling Rate', '功率损失': 'Power Loss',
'上次清洗日期': 'Last Clean', '建议清洗日期': 'Next Clean',
'工单任务': 'Work Order', '工单标题': 'Order Title', '工单类型': 'Order Type', '工单状态': 'Order Status',
'执行开始时间': 'Exec Start Time', '关联设备': 'Related Device', '关联区域': 'Related Area', '当前状态': 'Current Status',
'任务描述': 'Task Description', '附件素材': 'Attachments', '执行工单': 'Execute Order', '修改': 'Modify', '挂起': 'Suspend',
'完成': 'Complete', '派单': 'Dispatch', '修改工单': 'Modify Order', '工单派单': 'Dispatch Order', '近7天全部关联告警': '7d Alerts',
'请输入工单标题': 'Enter Title', '请选择工单类型': 'Select Type', '请选择优先级': 'Select Priority', '确认修改': 'Confirm Modify',
'确认派单': 'Confirm Dispatch', '确认执行': 'Confirm Execute', '确认完成': 'Confirm Complete', '选择接收人员': 'Select Receiver',
'请选择派单人员': 'Select Dispatcher', '选择执行设备': 'Select Device', '确定挂起?': 'Suspend?', '已创建': 'Created',
'执行中': 'In Progress', '已挂起': 'Suspended', '已完成': 'Completed', '待处理': 'Pending', '高': 'High', '中': 'Medium', '低': 'Low',
'AI 建议与关联信息': 'AI Suggestion', '建议携带': 'Suggested Equipment', '红外热像设备': 'Thermal Camera',
'优先复检组件': 'Prioritize Recheck', '预计影响发电': 'Est Impact', '关联告警趋势': 'Alert Trend', '关联告警列表': 'Alert List',
'近7天无告警记录': 'No alerts in 7d', '近 7 天': '(7d)', '高告警': 'High', '中告警': 'Medium', '低告警': 'Low',
'巡检': 'Inspection', '检修': 'Maintenance', '清洗': 'Cleaning', '维修': 'Repair', '运维': 'O&M', 'AI': 'AI',
'割草机故障': 'Mower Fault', '无人机故障': 'UAV Fault', '光伏组件故障': 'PV Module Fault', '清洗': 'Clean', '维护': 'Maintain',
'维修': 'Repair', '其他': 'Other',
'报表中心': 'Report Center', '发电量报表': 'Generation Report', '设备报表': 'Device Report', '告警报表': 'Alert Report',
'工单报表': 'Work Order Report', '巡检报表': 'Inspection Report', '清洗报表': 'Cleaning Report', '效率报表': 'Efficiency Report',
'自定义报表': 'Custom Report', '日报': 'Daily', '周报': 'Weekly', '月报': 'Monthly', '季报': 'Quarterly', '年报': 'Yearly',
'日期范围': 'Date Range', '生成报表': 'Generate Report', '导出报表': 'Export Report', '总发电量': 'Total Generation',
'峰值功率': 'Peak Power', '平均功率': 'Avg Power', '告警总数': 'Total Alerts', '工单总数': 'Total Orders', '完成率': 'Completion',
'平均效率': 'Avg Efficiency',
'系统设置': 'System Settings', '基础设置': 'Basic Settings', '用户管理': 'User Management', '菜单管理': 'Menu Management',
'组织管理': 'Organization Management', '场站管理': 'Station Management', '通知策略': 'Notice Policy', '设备接入': 'Device Access',
'数据与安全': 'Data & Safety', '系统维护': 'System Maintenance', '在线用户': 'Online Users', '设备管理': 'Device Management',
'日志管理': 'Log Management', '视频管理': 'Video Management', '系统名称': 'System Name', '系统版本': 'System Version',
'系统描述': 'System Description', '默认语言': 'Default Language', '系统Logo': 'System Logo', '主题': 'Theme', '浅色': 'Light',
'深色': 'Dark', '跟随系统': 'Follow System', '用户账号': 'Username', '用户昵称': 'Nickname', '手机号': 'Phone', '邮箱': 'Email',
'角色': 'Role', '密码': 'Password', '不填则不修改': 'Leave blank to keep', '语言': 'Language', '分配角色': 'Assign Role',
'性别': 'Gender', '状态': 'Status', '新增用户': 'Add User', '编辑用户': 'Edit User', '重置密码': 'Reset Password',
'确定将密码重置为 123456 吗?': 'Reset password to 123456?', '密码已重置为:123456': 'Password reset to 123456',
'中文': 'Chinese', '英语': 'English', '菜单名称': 'Menu Name', '菜单类型': 'Menu Type', '路由地址': 'Route Path',
'菜单图标': 'Menu Icon', '排序': 'Sort', '权限标识': 'Permission', '上级菜单': 'Parent Menu', '目录': 'Directory',
'菜单': 'Menu', '按钮': 'Button', '组织名称': 'Org Name', '组织类型': 'Org Type', '上级组织': 'Parent Org', '负责人': 'Leader',
'联系电话': 'Contact', '场站名称': 'Station Name', '场站类型': 'Station Type', '场站地址': 'Station Address',
'装机容量': 'Capacity', '场站状态': 'Station Status', '经度': 'Longitude', '纬度': 'Latitude', '时区': 'Timezone',
'日志类型': 'Log Type', '登录日志': 'Login Log', '操作日志': 'Operation Log', '错误日志': 'Error Log', '系统日志': 'System Log',
'操作人': 'Operator', '操作内容': 'Content', '操作时间': 'Time', 'IP地址': 'IP', '视频名称': 'Video Name', '视频地址': 'Video URL',
'视频类型': 'Video Type', '视频状态': 'Video Status', '备份数据库': 'Backup DB', '清除缓存': 'Clear Cache',
'系统信息': 'System Info', '磁盘使用': 'Disk', '内存使用': 'Memory', 'CPU使用率': 'CPU', '服务器状态': 'Server Status',
'运行时长': 'Uptime', '接入协议': 'Protocol', '接入状态': 'Status', '接入时间': 'Time', 'SN码': 'SN', '验证码': 'Verify Code',
'绑定设备': 'Bind Device', '错误编码': 'Error Code', '错误名称': 'Error Name', '错误来源': 'Error Source', '错误等级': 'Error Level',
'错误描述': 'Error Desc', '校验字段': 'Check Field', '对比规则': 'Compare Rule', '对比数值': 'Compare Value',
'区间最小值': 'Min', '区间最大值': 'Max', '请输入错误编码': 'Enter Error Code', '请输入错误名称': 'Enter Error Name',
'请选择来源': 'Select Source', '请选择等级': 'Select Level', '请选择错误来源': 'Select Error Source',
'请选择错误等级': 'Select Error Level', '请填写校验字段名': 'Enter Field', '请选择对比规则': 'Select Rule',
'请填写最小值': 'Enter Min', '请填写最大值': 'Enter Max', '请填写对比值': 'Enter Value',
'例如:ERR-ROBOT-001': 'e.g. ERR-ROBOT-001', '如:battery、speed、temperature': 'e.g. battery, speed',
'最小值': 'Min', '最大值': 'Max', '数值/文本,多个用逗号分隔': 'comma separated', '详细描述触发该错误的场景': 'Describe scenario',
'出现该错误后的处理方案': 'Handling plan', '新增错误规则': 'Add Error Rule', '编辑错误规则': 'Edit Error Rule',
'查看错误规则详情': 'View Error Rule', '确定要删除错误 "': 'Delete error "', '获取错误列表失败': 'Get error list failed',
'操作失败': 'Operation failed',
'个人中心': 'Profile', '获取个人信息失败': 'Get profile failed', '修改成功': 'Modified', '修改失败': 'Modify failed',
'密码修改成功': 'Password changed', '头像上传成功': 'Avatar uploaded', '上传失败': 'Upload failed',
'两次输入的密码不一致': 'Passwords mismatch', '更换头像': 'Change Avatar', '用户名称': 'Username', '用户昵称': 'Nickname',
'手机号码': 'Phone', '用户邮箱': 'Email', '所属角色': 'Role', '旧密码': 'Old Password', '新密码': 'New Password',
'确认密码': 'Confirm Password', '请输入用户昵称': 'Enter Nickname', '请输入手机号码': 'Enter Phone', '保存修改': 'Save Changes',
'确认修改': 'Confirm Modify',
'用户管理': 'User Management', '用户账号': 'Username', '用户昵称': 'Nickname', '手机号': 'Phone', '邮箱': 'Email',
'角色': 'Role', '状态': 'Status', '操作': 'Actions', '编辑': 'Edit', '删除': 'Delete', '用户账号': 'Username',
'用户昵称': 'Nickname', '手机号码': 'Phone', '密码': 'Password', '语言': 'Language', '分配角色': 'Assign Role',
'状态': 'Status', '性别': 'Gender', '不填则不修改': 'Leave blank', '中文': 'Chinese', '英语': 'English', '正常': 'Normal',
'停用': 'Disabled', '男': 'Male', '女': 'Female', '未知': 'Unknown', '批量删除(已选 ': 'Batch delete (', '重置密码': 'Reset Password',
'密码已重置为:123456': 'Password reset to 123456', '确定将密码重置为 123456 吗?': 'Reset to 123456?',
'角色管理': 'Role Management', '新增角色': 'Add Role', '角色名称': 'Role Name', '权限字符': 'Role Key', '排序': 'Sort',
'状态': 'Status', '备注': 'Remark', '操作': 'Actions', '编辑': 'Edit', '删除': 'Delete', '分配权限': 'Assign Permissions',
'数据权限': 'Data Scope', '菜单权限': 'Menu Permissions', '角色描述': 'Role Description', '正常': 'Normal', '停用': 'Disabled',
'异常': 'Abnormal',
'登录已过期,请重新登录': 'Session expired, please login again', '网络异常,请稍后重试': 'Network error, retry later',
'晴': 'Sunny', '多云': 'Cloudy', '阴': 'Overcast', '雨': 'Rainy',
};
// ---- collect referenced keys ----
const refRegex = /(?:i18n\.t|[^a-zA-Z]t)\(\s*['"`]([a-zA-Z][a-zA-Z0-9_.]*?)['"`]/g;
const files = walk(root);
const refKeys = new Set();
for (const f of files) {
const c = fs.readFileSync(f, 'utf8');
let m;
while ((m = refRegex.exec(c))) refKeys.add(m[1]);
}
// ---- helpers ----
function camelToWords(s) {
return s.replace(/([a-z])([A-Z])/g, '$1 $2').replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2').split(/[\s._]+/).filter(Boolean);
}
function inferZhFromKey(key) {
const leaf = key.split('.').pop();
const words = camelToWords(leaf).map(w => w.toLowerCase());
const out = [];
for (let i = 0; i < words.length; i++) {
const w = words[i];
let zh = lex[w] || tokZh[w] || (w.length <= 2 && w === w.toUpperCase() ? w : '');
if (w === 'no') zh = (i > 0 && NOUNS_WITH_NO.has(words[i - 1])) ? '编号' : '无';
if (!zh) continue;
if (out[out.length - 1] !== zh) out.push(zh);
}
return out.join('');
}
function inferEnFromKey(key) {
const leaf = key.split('.').pop();
const words = camelToWords(leaf);
const STOP = new Set(['of', 'and', 'the', 'to', 'in', 'on', 'with', 'a', 'an', 'or', 'for']);
const out = [];
for (let i = 0; i < words.length; i++) {
let w = words[i];
if (w.length <= 2 || w === w.toUpperCase()) { /* keep acronym */ }
else if (i > 0 && STOP.has(w.toLowerCase())) w = w.toLowerCase();
else w = w.charAt(0).toUpperCase() + w.slice(1);
if (out[out.length - 1] !== w) out.push(w);
}
return out.join(' ');
}
function translateZh(zh) {
if (zhEn[zh]) return zhEn[zh];
// greedy phrase substitution for multi-char matches
let s = zh;
// sort keys by length desc for greedy match
const keys = Object.keys(zhEn).sort((a, b) => b.length - a.length);
for (const k of keys) { if (k.length >= 2) s = s.split(k).join('\u0001' + zhEn[k] + '\u0001'); }
// now translate remaining single chars / unknown
const segs = s.split('\u0001');
const res = segs.map(seg => {
if (zhEn[seg]) return zhEn[seg];
// try per-char? skip unknown chars to avoid garbage
return seg;
});
return res.join(' ').replace(/\s+/g, ' ').trim();
}
// ---- determine additions ----
const zhAdd = {}; // ns -> {key: val}
const enAdd = {};
const zhCjk = {};
const enCjk = {};
function addKey(fullKey, zhVal, enVal, isCjk) {
const dot = fullKey.indexOf('.');
const ns = dot > -1 ? fullKey.slice(0, dot) : '';
const leaf = dot > -1 ? fullKey.slice(dot + 1) : fullKey;
const zmap = isCjk ? zhCjk : zhAdd;
const emap = isCjk ? enCjk : enAdd;
zmap[ns] = zmap[ns] || {};
emap[ns] = emap[ns] || {};
if (!(fullKey in zhMap)) zmap[ns][leaf] = zhVal;
if (!(fullKey in enMap)) emap[ns][leaf] = enVal;
}
// referenced keys missing
let missingCount = 0;
for (const k of refKeys) {
if (!(k in zhMap) && !(k in enMap)) {
missingCount++;
addKey(k, inferZhFromKey(k), inferEnFromKey(k));
}
}
// hardcoded chinese -> keys (reuse existing by value; else new key under a file-based ns)
// map file path to namespace
function nsForFile(f) {
const rel = f.replace(root + '\\', '').replace(/\\/g, '/');
const base = path.basename(f).replace(/\.(tsx|ts|jsx|js)$/, '');
// try to infer from existing namespaces by matching folder/file
const lower = rel.toLowerCase();
if (lower.includes('systemsetting')) return 'systemSetting';
if (lower.includes('devices/') || lower.includes('device')) return 'devices';
if (lower.includes('alerts')) return 'alerts';
if (lower.includes('aianalysis') || lower.includes('aidiagnos')) return 'aiAnalysis';
if (lower.includes('clean')) return 'clean';
if (lower.includes('report')) return 'report';
if (lower.includes('workorder')) return 'workOrder';
if (lower.includes('login')) return 'login';
if (lower.includes('profile')) return 'profile';
if (lower.includes('users')) return 'users';
if (lower.includes('roles')) return 'roles';
if (lower.includes('home')) return 'home';
if (lower.includes('video')) return 'video';
if (lower.includes('router') || lower.includes('layout')) return 'layout';
return 'page';
}
const CJK = /[一-鿿]/;
let cjkCount = 0;
const cjkSeen = new Set();
for (const f of files) {
const lines = fs.readFileSync(f, 'utf8').split('\n');
for (const line of lines) {
const trimmed = line.trim();
if (trimmed.startsWith('//') || trimmed.startsWith('*')) continue;
if (/console\./.test(line)) continue;
if (!CJK.test(line)) continue;
// extract CJK substrings that look like user-facing text (skip pure comment lines)
const matches = line.match(/[一-鿿][一-鿿,。、::()()\sA-Za-z0-9%·\-/]*[一-鿿]/g);
if (!matches) continue;
for (const m of matches) {
const txt = m.trim();
if (txt.length < 2) continue;
if (cjkSeen.has(txt)) continue;
cjkSeen.add(txt);
cjkCount++;
// reuse existing key if zh value equals this
let reuseKey = null;
for (const ek of Object.keys(zhMap)) { if (zhMap[ek] === txt) { reuseKey = ek; break; } }
if (reuseKey) continue; // already have it
const ns = nsForFile(f);
// build a key from file base + counter
const base = path.basename(f).replace(/\.(tsx|ts|jsx|js)$/, '').replace(/[^a-zA-Z0-9]/g, '');
const key = ns + '.' + base + '_' + (cjkSeen.size);
addKey(key, txt, translateZh(txt), true);
}
}
}
console.log('Referenced keys total:', refKeys.size);
console.log('Missing referenced keys (added):', missingCount);
console.log('Hardcoded CJK unique strings seen:', cjkCount);
console.log('New keys to add (zh):', Object.values(zhAdd).reduce((s, o) => s + Object.keys(o).length, 0));
// save additions for next step
fs.writeFileSync('tmp_missing.json', JSON.stringify({ zhAdd, enAdd }, null, 2));
fs.writeFileSync('tmp_cjk.json', JSON.stringify({ zhCjk, enCjk }, null, 2));
console.log('Wrote tmp_missing.json and tmp_cjk.json');