From 7a0470e331a978ee206cd8fcf3dfd5642f14eb83 Mon Sep 17 00:00:00 2001
From: Junjie <fallin.jie@qq.com>
Date: 星期二, 17 三月 2026 09:23:44 +0800
Subject: [PATCH] #

---
 src/main/resources/i18n/zh-CN/messages.properties        |  188 ++++++++
 src/main/webapp/static/js/devicePingLog/devicePingLog.js |  200 ++++++++-
 src/main/webapp/static/js/dashboard/dashboard.js         |  264 +++++++++++
 src/main/resources/i18n/en-US/messages.properties        |  188 ++++++++
 src/main/resources/i18n/zh-CN/legacy.properties          |   49 ++
 src/main/resources/i18n/en-US/legacy.properties          |   48 ++
 src/main/webapp/views/dashboard/dashboard.html           |  202 ++++----
 src/main/webapp/views/devicePingLog/devicePingLog.html   |  120 ++--
 8 files changed, 1,046 insertions(+), 213 deletions(-)

diff --git a/src/main/resources/i18n/en-US/legacy.properties b/src/main/resources/i18n/en-US/legacy.properties
index e43aa6d..5c33986 100644
--- a/src/main/resources/i18n/en-US/legacy.properties
+++ b/src/main/resources/i18n/en-US/legacy.properties
@@ -1081,6 +1081,54 @@
 鏂板绯荤粺閰嶇疆=Add System Configuration
 淇敼绯荤粺閰嶇疆=Edit System Configuration
 璇︽儏绯荤粺閰嶇疆=System Configuration Details
+
+# Dashboard and device ping pages
+绯荤粺浠〃鐩�=System Dashboard
+璁惧缃戠粶鍒嗘瀽=Device Network Analysis
+鐩戞帶鐢婚潰=Monitoring View
+鐩戞帶绯荤粺=Monitoring
+鍏ュ簱浠诲姟=Inbound Tasks
+鍑哄簱浠诲姟=Outbound Tasks
+鎵ц涓�=Running
+寰呬汉宸�=Manual Review
+宸插畬鎴�=Completed
+鏂板缓=New
+鐢熸垚鍏ュ簱浠诲姟=Create Inbound Task
+璁惧涓婅蛋=Device Moving Up
+璁惧鎼繍涓�=Device Handling
+璁惧鎼繍瀹屾垚=Device Handling Completed
+鍏ュ簱寰呬汉宸ュ洖婊�=Inbound Pending Manual Rollback
+鍏ュ簱瀹屾垚=Inbound Completed
+鍏ュ簱搴撳瓨鏇存柊=Inbound Inventory Updated
+鐢熸垚鍑哄簱浠诲姟=Create Outbound Task
+绔欑偣杩愯涓�=Station Running
+绔欑偣杩愯瀹屾垚=Station Run Completed
+鍑哄簱寰呬汉宸ュ洖婊�=Outbound Pending Manual Rollback
+鍑哄簱瀹屾垚=Outbound Completed
+鍑哄簱搴撳瓨鏇存柊=Outbound Inventory Updated
+鐢熸垚绉诲簱浠诲姟=Create Transfer Task
+绉诲簱寰呬汉宸ュ洖婊�=Transfer Pending Manual Rollback
+绉诲簱瀹屾垚=Transfer Completed
+杈撻�佺珯鐐�=Conveyor Stations
+鍙敤=Available
+宸茬鐢�=Disabled
+瓒呮椂=Timeout
+娉㈠姩=Unstable
+瓒呮椂/寮傚父=Timeout/Error
+鏈畾涔夌姸鎬�=Undefined Status
+鍏ㄩ儴鎺㈡祴鍧囪秴鏃�=All probes timed out
+鏆傛棤棰濆璇存槑=No additional details
+鎺㈡祴澶辫触=Probe failed
+璇锋眰瓒呮椂=Request timeout
+鎵句笉鍒颁富鏈�=Could not find host
+鏃犳硶璁块棶鐩爣涓绘満=Destination host unreachable
+100.0% 涓㈠け=100.0% packet loss
+regex\:^鐘舵��(\d+)$=Status $1
+regex\:^绔欑偣(\d+)$=Station $1
+regex\:^绔欑偣(\d+)\s*/\s*(.+)$=Station $1 / $2
+regex\:^鍫嗗灈鏈�#(\d+)$=Crane #$1
+regex\:^鍙屽伐浣�#(\d+)$=Dual Station #$1
+regex\:^RGV#(\d+)$=RGV #$1
 鏄惁Delete=Deleted
 绉� 鎴�=Tenant
 璐� 鍙�=Account
diff --git a/src/main/resources/i18n/en-US/messages.properties b/src/main/resources/i18n/en-US/messages.properties
index 834d853..606a9e1 100644
--- a/src/main/resources/i18n/en-US/messages.properties
+++ b/src/main/resources/i18n/en-US/messages.properties
@@ -213,6 +213,194 @@
 legacy.regex.stationDeviceLink=Click to remove mapping: Station {0} -> Device {1}
 deviceLogs.visualizationPrefix=Device Logs - 
 deviceLogs.downloadDialogTitle=Downloading file
+dashboard.title=System Dashboard
+dashboard.monitorView=Monitoring View
+dashboard.openMonitor=Open Monitoring View
+dashboard.refreshNow=Refresh Now
+dashboard.overviewKicker=Overview
+dashboard.overviewNote=System and refresh cadence
+dashboard.systemStatusLabel=System Status
+dashboard.systemRunning=Running
+dashboard.systemPaused=Paused
+dashboard.systemStatusDesc=Current status of the WCS main service
+dashboard.lastRefreshLabel=Last Refresh
+dashboard.lastRefreshDesc=Time when the latest aggregated data was generated
+dashboard.autoRefreshLabel=Auto Refresh
+dashboard.autoRefreshValue=Refresh in {0}s
+dashboard.autoRefreshDesc=Countdown to the next automatic refresh
+dashboard.coreMetricsKicker=Core Metrics
+dashboard.coreMetricsNote=Tasks, devices, and AI overview
+dashboard.taskTotalLabel=Total Tasks
+dashboard.taskTotalDesc=Running now {0}
+dashboard.deviceOnlineLabel=Online Devices
+dashboard.deviceOnlineDesc=Total {0}, alarms {1}
+dashboard.aiTokenTotalLabel=AI Total Tokens
+dashboard.aiTokenTotalDesc=Aggregated across AI sessions
+dashboard.aiCallTotalLabel=LLM Calls
+dashboard.aiCallTotalDesc=Latest run details are included in the AI section below
+dashboard.taskPanelTitle=Task Overview
+dashboard.taskPanelDesc=Quickly assess current workload pressure by task type, execution stage, and recent flow records.
+dashboard.taskRunningLabel=Running
+dashboard.taskRunningHint=Tasks currently in progress
+dashboard.taskManualLabel=Manual Review
+dashboard.taskManualHint=Requires manual attention or rollback
+dashboard.taskCompletedLabel=Completed
+dashboard.taskCompletedHint=Finished or posted
+dashboard.taskNewLabel=New
+dashboard.taskNewHint=Just entered the dispatch flow
+dashboard.taskDirectionChartTitle=Task Type Distribution
+dashboard.taskStageChartTitle=Task Stage Overview
+dashboard.recentPanelTitle=Recent Tasks
+dashboard.recentPanelDesc=Quickly tell whether tasks are piling up, whether devices have taken over, and where recent tasks are headed.
+dashboard.recentEmpty=No recent tasks
+dashboard.column.workNo=Work No.
+dashboard.column.taskType=Task Type
+dashboard.column.status=Status
+dashboard.column.source=Source
+dashboard.column.target=Target
+dashboard.column.device=Execution Device
+dashboard.column.barcode=Barcode
+dashboard.column.updateTime=Last Updated
+dashboard.aiPanelTitle=AI Activity
+dashboard.aiPanelDesc=Review cumulative AI tokens, LLM call volume, and route availability and cooldown status.
+dashboard.availableRoutesTag=Available routes {0}
+dashboard.aiTokenCardLabel=Total Tokens
+dashboard.aiTokenCardHint=Prompt + Completion
+dashboard.aiAskCountLabel=Conversation Turns
+dashboard.aiAskCountHint=Total AI conversation turns
+dashboard.aiLlmCallLabel=LLM Calls
+dashboard.aiLlmCallHint=Success {0} / Failed {1}
+dashboard.aiSessionCountLabel=Sessions
+dashboard.aiSessionCountHint=Last call {0}
+dashboard.aiRouteChartTitle=AI Route Status
+dashboard.aiRouteDesc=Model {0}, priority {1}
+dashboard.aiRouteResult=Success {0} / Failed {1}
+dashboard.aiRouteLastUsed=Last used {0}
+dashboard.aiRouteEmpty=No AI route data
+dashboard.devicePanelTitle=Device Overview
+dashboard.devicePanelDesc=Summarize the online, busy, and alarm states of conveyor stations, cranes, dual-station cranes, and RGVs.
+dashboard.deviceOnlineRate=Online rate {0}
+dashboard.deviceTotalLabel=Total Devices
+dashboard.deviceTotalHint=Enabled configured devices
+dashboard.deviceOnlineCardLabel=Online Devices
+dashboard.deviceOnlineCardHint=Devices currently reachable
+dashboard.deviceBusyLabel=Busy Devices
+dashboard.deviceBusyHint=Devices currently carrying tasks
+dashboard.deviceAlarmLabel=Alarm Devices
+dashboard.deviceAlarmHint=Devices with blockage or alarm status
+dashboard.deviceTypeChartTitle=Device Online Distribution
+dashboard.deviceTypeDesc=Online {0} / Total {1}, offline {2}
+dashboard.deviceBusyTag=Busy {0}
+dashboard.deviceAlarmTag=Alarm {0}
+dashboard.networkPanelDesc=Summarize connectivity, latency, and abnormal devices from the latest ping samples to quickly spot network fluctuations.
+dashboard.networkAttentionTag=Attention {0}
+dashboard.networkViewDetail=View Details
+dashboard.networkOkLabel=Normal
+dashboard.networkOkHint=Latest sample status is OK
+dashboard.networkUnstableLabel=Unstable
+dashboard.networkUnstableHint=Some probes succeeded
+dashboard.networkOfflineLabel=Timeout/Error
+dashboard.networkNoDataLabel=No Data
+dashboard.networkNoDataHint=No data {0}
+dashboard.networkAvgLatencyLabel=Average Latency
+dashboard.networkPeakLatencyHint=Peak {0}
+dashboard.networkStatusChartTitle=Connectivity Status Distribution
+dashboard.networkAvgLatencyTag=Avg {0}
+dashboard.networkLatestSampleTag=Latest sample {0}
+dashboard.networkHealthyTitle=Network probing is stable
+dashboard.networkHealthyDesc={0} devices are included, and no timeouts or fluctuations were found in the latest round.
+dashboard.networkHealthyOk=Normal {0}
+dashboard.networkHealthyAvg=Avg {0}
+dashboard.networkHealthyPeak=Peak {0}
+dashboard.networkEmpty=No device network samples
+dashboard.loadingTitle=Loading dashboard
+dashboard.loadingDesc=Aggregating task, device, and AI runtime data. Please wait...
+dashboard.loadFailed=Failed to load dashboard data
+dashboard.loadFailedDetail=Failed to load dashboard data. Please check the API status.
+dashboard.deviceOnlineLegend=Online
+dashboard.deviceOfflineLegend=Offline
+dashboard.chartCenter.availableRoutes=Available Routes
+dashboard.chartCenter.attentionDevices=Devices Requiring Attention
+dashboard.systemDefaultPacketSize=System Default
+dashboard.networkSampling=Sampling {0} ms / Timeout {1} ms / {2} probes per sample / Packet size {3}
+dashboard.taskDirectionInbound=Inbound Tasks
+dashboard.taskDirectionOutbound=Outbound Tasks
+dashboard.taskDirectionMove=Transfer Tasks
+dashboard.aiRouteStatusAvailable=Available
+dashboard.aiRouteStatusCooling=Cooling
+dashboard.aiRouteStatusDisabled=Disabled
+devicePingLog.title=Device Network Analysis
+devicePingLog.pageMeta=Packet size {0}, {1}
+devicePingLog.summary.totalDevices=Total Devices
+devicePingLog.summary.totalDevicesHint=Devices with configured IPs
+devicePingLog.summary.ok=Normal
+devicePingLog.summary.okHint=Latest sample status is OK
+devicePingLog.summary.unstable=Unstable
+devicePingLog.summary.unstableHint=Some probes succeeded
+devicePingLog.summary.offline=Timeout/Error
+devicePingLog.summary.offlineHint=Latest sample is unreachable
+devicePingLog.summary.noData=No Data
+devicePingLog.summary.noDataHint=No persisted samples yet
+devicePingLog.summary.avgLatency=Overall Average Latency
+devicePingLog.summary.peakLatency=Peak {0}
+devicePingLog.overviewTitle=Device Overview
+devicePingLog.refresh=Refresh
+devicePingLog.filter.deviceType=Device Type
+devicePingLog.filter.all=All
+devicePingLog.filter.keyword=Keyword
+devicePingLog.filter.keywordPlaceholder=Device No. / IP
+devicePingLog.overviewCount=Overview devices {0}
+devicePingLog.column.device=Device
+devicePingLog.column.status=Status
+devicePingLog.column.successRate=Success Rate
+devicePingLog.column.avg=Avg
+devicePingLog.column.min=Min
+devicePingLog.column.max=Max
+devicePingLog.column.updateTime=Updated At
+devicePingLog.column.message=Message
+devicePingLog.column.action=Action
+devicePingLog.viewDetail=View Details
+devicePingLog.detailTitle=Device Details
+devicePingLog.detailEmpty=Select a device from the overview above to inspect second-level details.
+devicePingLog.filter.timeRange=Time Range
+devicePingLog.filter.rangeSeparator=to
+devicePingLog.filter.start=Start
+devicePingLog.filter.end=End
+devicePingLog.quickRange.30m=30 min
+devicePingLog.quickRange.1h=1 hr
+devicePingLog.quickRange.6h=6 hr
+devicePingLog.query=Query
+devicePingLog.detail.status=Status
+devicePingLog.detail.packetSize=Packet Size
+devicePingLog.detail.successRate=Success Rate
+devicePingLog.detail.avg=Avg
+devicePingLog.detail.min=Min
+devicePingLog.detail.max=Max
+devicePingLog.chart.latencyTitle=Latency
+devicePingLog.chart.empty=No second-level samples in the current range
+devicePingLog.chart.availabilityTitle=Success Rate / Fail Count
+devicePingLog.alertColumn.time=Time
+devicePingLog.alertColumn.status=Status
+devicePingLog.alertColumn.message=Message
+devicePingLog.samplingConfigText=Sampling {0} ms / Timeout {1} ms / {2} probes per sample
+devicePingLog.optionsLoadFailed=Failed to load device configuration
+devicePingLog.overviewLoadFailed=Failed to load overview data
+devicePingLog.selectTimeRange=Please select a time range
+devicePingLog.invalidDevice=Invalid device information
+devicePingLog.detailLoadFailed=Failed to load device details
+devicePingLog.chart.latency.avg=Avg
+devicePingLog.chart.latency.min=Min
+devicePingLog.chart.latency.max=Max
+devicePingLog.chart.availability.failAxis=Failures
+devicePingLog.chart.availability.successRateAxis=Success Rate %
+devicePingLog.chart.availability.failCount=Fail Count
+devicePingLog.chart.availability.successRate=Success Rate
+devicePingLog.systemDefaultPacketSize=System Default
+devicePingLog.status.ok=Normal
+devicePingLog.status.unstable=Unstable
+devicePingLog.status.timeout=Timeout
+devicePingLog.status.error=Error
+devicePingLog.status.noData=No Data
 llm.logsTitle=LLM Call Logs
 llm.logDetailTitle=Log Details
 llm.logDetailPrefix=Log Details - 
diff --git a/src/main/resources/i18n/zh-CN/legacy.properties b/src/main/resources/i18n/zh-CN/legacy.properties
index 2e5329b..ff78c24 100644
--- a/src/main/resources/i18n/zh-CN/legacy.properties
+++ b/src/main/resources/i18n/zh-CN/legacy.properties
@@ -161,3 +161,52 @@
 鏂板绯荤粺閰嶇疆=鏂板绯荤粺閰嶇疆
 淇敼绯荤粺閰嶇疆=淇敼绯荤粺閰嶇疆
 璇︽儏绯荤粺閰嶇疆=璇︽儏绯荤粺閰嶇疆
+
+# Dashboard and device ping pages
+绯荤粺浠〃鐩�=绯荤粺浠〃鐩�
+璁惧缃戠粶鍒嗘瀽=璁惧缃戠粶鍒嗘瀽
+鐩戞帶鐢婚潰=鐩戞帶鐢婚潰
+鐩戞帶绯荤粺=鐩戞帶绯荤粺
+鍏ュ簱浠诲姟=鍏ュ簱浠诲姟
+鍑哄簱浠诲姟=鍑哄簱浠诲姟
+绉诲簱浠诲姟=绉诲簱浠诲姟
+鎵ц涓�=鎵ц涓�
+寰呬汉宸�=寰呬汉宸�
+宸插畬鎴�=宸插畬鎴�
+鏂板缓=鏂板缓
+鐢熸垚鍏ュ簱浠诲姟=鐢熸垚鍏ュ簱浠诲姟
+璁惧涓婅蛋=璁惧涓婅蛋
+璁惧鎼繍涓�=璁惧鎼繍涓�
+璁惧鎼繍瀹屾垚=璁惧鎼繍瀹屾垚
+鍏ュ簱寰呬汉宸ュ洖婊�=鍏ュ簱寰呬汉宸ュ洖婊�
+鍏ュ簱瀹屾垚=鍏ュ簱瀹屾垚
+鍏ュ簱搴撳瓨鏇存柊=鍏ュ簱搴撳瓨鏇存柊
+鐢熸垚鍑哄簱浠诲姟=鐢熸垚鍑哄簱浠诲姟
+绔欑偣杩愯涓�=绔欑偣杩愯涓�
+绔欑偣杩愯瀹屾垚=绔欑偣杩愯瀹屾垚
+鍑哄簱寰呬汉宸ュ洖婊�=鍑哄簱寰呬汉宸ュ洖婊�
+鍑哄簱瀹屾垚=鍑哄簱瀹屾垚
+鍑哄簱搴撳瓨鏇存柊=鍑哄簱搴撳瓨鏇存柊
+鐢熸垚绉诲簱浠诲姟=鐢熸垚绉诲簱浠诲姟
+绉诲簱寰呬汉宸ュ洖婊�=绉诲簱寰呬汉宸ュ洖婊�
+绉诲簱瀹屾垚=绉诲簱瀹屾垚
+杈撻�佺珯鐐�=杈撻�佺珯鐐�
+鍙敤=鍙敤
+宸茬鐢�=宸茬鐢�
+瓒呮椂=瓒呮椂
+娉㈠姩=娉㈠姩
+瓒呮椂/寮傚父=瓒呮椂/寮傚父
+鏈畾涔夌姸鎬�=鏈畾涔夌姸鎬�
+鍏ㄩ儴鎺㈡祴鍧囪秴鏃�=鍏ㄩ儴鎺㈡祴鍧囪秴鏃�
+鏆傛棤棰濆璇存槑=鏆傛棤棰濆璇存槑
+鎺㈡祴澶辫触=鎺㈡祴澶辫触
+璇锋眰瓒呮椂=璇锋眰瓒呮椂
+鎵句笉鍒颁富鏈�=鎵句笉鍒颁富鏈�
+鏃犳硶璁块棶鐩爣涓绘満=鏃犳硶璁块棶鐩爣涓绘満
+100.0% 涓㈠け=100.0% 涓㈠け
+regex\:^鐘舵��(\d+)$=鐘舵��$1
+regex\:^绔欑偣(\d+)$=绔欑偣$1
+regex\:^绔欑偣(\d+)\s*/\s*(.+)$=绔欑偣$1 / $2
+regex\:^鍫嗗灈鏈�#(\d+)$=鍫嗗灈鏈�#$1
+regex\:^鍙屽伐浣�#(\d+)$=鍙屽伐浣�#$1
+regex\:^RGV#(\d+)$=RGV#$1
diff --git a/src/main/resources/i18n/zh-CN/messages.properties b/src/main/resources/i18n/zh-CN/messages.properties
index 058262b..231d7ce 100644
--- a/src/main/resources/i18n/zh-CN/messages.properties
+++ b/src/main/resources/i18n/zh-CN/messages.properties
@@ -213,6 +213,194 @@
 legacy.regex.stationDeviceLink=鐐瑰嚮鍒犻櫎鍏宠仈: 绔欑偣 {0} -> 璁惧 {1}
 deviceLogs.visualizationPrefix=鏃ュ織鍙鍖� - 
 deviceLogs.downloadDialogTitle=鏂囦欢涓嬭浇涓�
+dashboard.title=绯荤粺浠〃鐩�
+dashboard.monitorView=鐩戞帶鐢婚潰
+dashboard.openMonitor=鎵撳紑鐩戞帶鐢婚潰
+dashboard.refreshNow=绔嬪嵆鍒锋柊
+dashboard.overviewKicker=鐘舵�佹瑙�
+dashboard.overviewNote=绯荤粺涓庡埛鏂拌妭濂�
+dashboard.systemStatusLabel=绯荤粺鐘舵��
+dashboard.systemRunning=杩愯涓�
+dashboard.systemPaused=宸叉殏鍋�
+dashboard.systemStatusDesc=WCS 涓绘湇鍔″綋鍓嶇姸鎬�
+dashboard.lastRefreshLabel=鏈�杩戝埛鏂�
+dashboard.lastRefreshDesc=鏈�杩戜竴娆¤仛鍚堟暟鎹敓鎴愭椂闂�
+dashboard.autoRefreshLabel=鑷姩鍒锋柊
+dashboard.autoRefreshValue={0}s 鍚庡埛鏂�
+dashboard.autoRefreshDesc=椤甸潰鑷姩鏇存柊鍊掕鏃�
+dashboard.coreMetricsKicker=鏍稿績鎸囨爣
+dashboard.coreMetricsNote=浠诲姟銆佽澶囦笌 AI 鎬昏
+dashboard.taskTotalLabel=浠诲姟鎬绘暟
+dashboard.taskTotalDesc=褰撳墠鎵ц涓� {0}
+dashboard.deviceOnlineLabel=鍦ㄧ嚎璁惧
+dashboard.deviceOnlineDesc=鎬昏澶� {0}锛屽憡璀� {1}
+dashboard.aiTokenTotalLabel=AI 绱 Tokens
+dashboard.aiTokenTotalDesc=鎸� AI 浼氳瘽绱缁熻
+dashboard.aiCallTotalLabel=LLM 璋冪敤娆℃暟
+dashboard.aiCallTotalDesc=鏈�杩戜竴杞繍琛屾儏鍐靛凡绾冲叆涓嬫柟 AI 鍖哄煙
+dashboard.taskPanelTitle=浠诲姟鎬佸娍
+dashboard.taskPanelDesc=浠庝换鍔$被鍨嬨�佹墽琛岄樁娈靛拰鏈�杩戞祦杞褰曞揩閫熷垽鏂綋鍓嶄綔涓氬帇鍔涖��
+dashboard.taskRunningLabel=鎵ц涓�
+dashboard.taskRunningHint=褰撳墠姝e湪娴佽浆鐨勪换鍔�
+dashboard.taskManualLabel=寰呬汉宸�
+dashboard.taskManualHint=闇�浜哄伐鍏虫敞鎴栧洖婊�
+dashboard.taskCompletedLabel=宸插畬鎴�
+dashboard.taskCompletedHint=宸茬粡瀹屾垚鎴栬惤璐�
+dashboard.taskNewLabel=鏂板缓
+dashboard.taskNewHint=鍒氳繘鍏ヨ皟搴︽祦绋�
+dashboard.taskDirectionChartTitle=浠诲姟绫诲瀷鍒嗗竷
+dashboard.taskStageChartTitle=浠诲姟闃舵姒傝
+dashboard.recentPanelTitle=鏈�杩戜换鍔�
+dashboard.recentPanelDesc=甯姪蹇�熷垽鏂换鍔℃槸鍚﹀爢绉�佹槸鍚﹁璁惧鎺ユ墜锛屼互鍙婃渶杩戠殑浠诲姟鐩爣浣嶇疆銆�
+dashboard.recentEmpty=鏆傛棤浠诲姟璁板綍
+dashboard.column.workNo=浠诲姟鍙�
+dashboard.column.taskType=浠诲姟绫诲瀷
+dashboard.column.status=鐘舵��
+dashboard.column.source=鏉ユ簮
+dashboard.column.target=鐩爣
+dashboard.column.device=鎵ц璁惧
+dashboard.column.barcode=鏉$爜
+dashboard.column.updateTime=鏈�杩戞洿鏂版椂闂�
+dashboard.aiPanelTitle=AI 杩愯鎯呭喌
+dashboard.aiPanelDesc=鏌ョ湅 AI 浼氳瘽绱 Tokens銆丩LM 璋冪敤閲忥紝浠ュ強璺敱鐨勫彲鐢ㄤ笌鍐峰嵈鐘舵�併��
+dashboard.availableRoutesTag=鍙敤璺敱 {0}
+dashboard.aiTokenCardLabel=绱 Tokens
+dashboard.aiTokenCardHint=Prompt + Completion
+dashboard.aiAskCountLabel=鎻愰棶杞
+dashboard.aiAskCountHint=AI 瀵硅瘽绱杞
+dashboard.aiLlmCallLabel=LLM 璋冪敤
+dashboard.aiLlmCallHint=鎴愬姛 {0} / 澶辫触 {1}
+dashboard.aiSessionCountLabel=浼氳瘽鏁�
+dashboard.aiSessionCountHint=鏈�杩戣皟鐢� {0}
+dashboard.aiRouteChartTitle=AI 璺敱鐘舵��
+dashboard.aiRouteDesc=妯″瀷 {0}锛屼紭鍏堢骇 {1}
+dashboard.aiRouteResult=鎴愬姛 {0} / 澶辫触 {1}
+dashboard.aiRouteLastUsed=鏈�杩戜娇鐢� {0}
+dashboard.aiRouteEmpty=鏆傛棤 AI 璺敱鏁版嵁
+dashboard.devicePanelTitle=璁惧鎬佸娍
+dashboard.devicePanelDesc=姹囨�昏緭閫佺珯鐐广�佸爢鍨涙満銆佸弻宸ヤ綅鍫嗗灈鏈轰笌 RGV 鐨勫湪绾裤�佸繖纰屽拰鍛婅鎯呭喌銆�
+dashboard.deviceOnlineRate=鍦ㄧ嚎鐜� {0}
+dashboard.deviceTotalLabel=璁惧鎬绘暟
+dashboard.deviceTotalHint=宸插惎鐢ㄩ厤缃澶�
+dashboard.deviceOnlineCardLabel=鍦ㄧ嚎璁惧
+dashboard.deviceOnlineCardHint=瀹炴椂杩為�氳澶囨暟閲�
+dashboard.deviceBusyLabel=蹇欑璁惧
+dashboard.deviceBusyHint=褰撳墠鎵胯浇浠诲姟鐨勮澶�
+dashboard.deviceAlarmLabel=鍛婅璁惧
+dashboard.deviceAlarmHint=鍚樆濉炴垨鎶ヨ鐘舵��
+dashboard.deviceTypeChartTitle=璁惧鍦ㄧ嚎鍒嗗竷
+dashboard.deviceTypeDesc=鍦ㄧ嚎 {0} / 鎬绘暟 {1}锛岀绾� {2}
+dashboard.deviceBusyTag=蹇欑 {0}
+dashboard.deviceAlarmTag=鍛婅 {0}
+dashboard.networkPanelDesc=姹囨�绘渶鏂� Ping 鏍锋湰鐨勮繛閫氭�с�佸欢杩熶笌寮傚父璁惧锛屽府鍔╁揩閫熷彂鐜扮綉缁滄尝鍔ㄣ��
+dashboard.networkAttentionTag=闇�鍏虫敞 {0}
+dashboard.networkViewDetail=鏌ョ湅鏄庣粏
+dashboard.networkOkLabel=姝e父
+dashboard.networkOkHint=鏈�鏂版牱鏈姸鎬� OK
+dashboard.networkUnstableLabel=娉㈠姩
+dashboard.networkUnstableHint=閮ㄥ垎鎺㈡祴鎴愬姛
+dashboard.networkOfflineLabel=瓒呮椂/寮傚父
+dashboard.networkNoDataLabel=鏆傛棤鏁版嵁
+dashboard.networkNoDataHint=鏆傛棤鏁版嵁 {0}
+dashboard.networkAvgLatencyLabel=骞冲潎寤惰繜
+dashboard.networkPeakLatencyHint=宄板�� {0}
+dashboard.networkStatusChartTitle=杩為�氱姸鎬佸垎甯�
+dashboard.networkAvgLatencyTag=骞冲潎 {0}
+dashboard.networkLatestSampleTag=鏈�杩戞牱鏈� {0}
+dashboard.networkHealthyTitle=褰撳墠缃戠粶鎺㈡祴绋冲畾
+dashboard.networkHealthyDesc=宸茬撼鍏� {0} 鍙拌澶囷紝鏈�杩戜竴杞湭鍙戠幇瓒呮椂鎴栨尝鍔ㄣ��
+dashboard.networkHealthyOk=姝e父 {0}
+dashboard.networkHealthyAvg=骞冲潎 {0}
+dashboard.networkHealthyPeak=宄板�� {0}
+dashboard.networkEmpty=鏆傛棤璁惧缃戠粶鏍锋湰
+dashboard.loadingTitle=姝e湪鍔犺浇浠〃鐩�
+dashboard.loadingDesc=姹囨�讳换鍔°�佽澶囦笌 AI 杩愯鏁版嵁锛岃绋嶅��...
+dashboard.loadFailed=浠〃鐩樻暟鎹姞杞藉け璐�
+dashboard.loadFailedDetail=浠〃鐩樻暟鎹姞杞藉け璐ワ紝璇锋鏌ユ帴鍙g姸鎬�
+dashboard.deviceOnlineLegend=鍦ㄧ嚎
+dashboard.deviceOfflineLegend=绂荤嚎
+dashboard.chartCenter.availableRoutes=鍙敤璺敱
+dashboard.chartCenter.attentionDevices=闇�鍏虫敞璁惧
+dashboard.systemDefaultPacketSize=绯荤粺榛樿
+dashboard.networkSampling=閲囨牱 {0} ms / 瓒呮椂 {1} ms / 姣忔牱鏈� {2} 娆� / 鍖呭ぇ灏� {3}
+dashboard.taskDirectionInbound=鍏ュ簱浠诲姟
+dashboard.taskDirectionOutbound=鍑哄簱浠诲姟
+dashboard.taskDirectionMove=绉诲簱浠诲姟
+dashboard.aiRouteStatusAvailable=鍙敤
+dashboard.aiRouteStatusCooling=鍐峰嵈涓�
+dashboard.aiRouteStatusDisabled=宸茬鐢�
+devicePingLog.title=璁惧缃戠粶鍒嗘瀽
+devicePingLog.pageMeta=鍖呭ぇ灏� {0}锛寋1}
+devicePingLog.summary.totalDevices=璁惧鎬绘暟
+devicePingLog.summary.totalDevicesHint=宸查厤缃� IP 鐨勮澶�
+devicePingLog.summary.ok=姝e父
+devicePingLog.summary.okHint=鏈�杩戞牱鏈姸鎬� OK
+devicePingLog.summary.unstable=娉㈠姩
+devicePingLog.summary.unstableHint=閮ㄥ垎鎺㈡祴鎴愬姛
+devicePingLog.summary.offline=瓒呮椂/寮傚父
+devicePingLog.summary.offlineHint=鏈�杩戞牱鏈笉鍙揪
+devicePingLog.summary.noData=鏆傛棤鏁版嵁
+devicePingLog.summary.noDataHint=杩樻病鏈夎惤鐩樻牱鏈�
+devicePingLog.summary.avgLatency=鏁翠綋骞冲潎寤惰繜
+devicePingLog.summary.peakLatency=宄板�� {0}
+devicePingLog.overviewTitle=璁惧鎬昏
+devicePingLog.refresh=鍒锋柊
+devicePingLog.filter.deviceType=璁惧绫诲瀷
+devicePingLog.filter.all=鍏ㄩ儴
+devicePingLog.filter.keyword=鍏抽敭瀛�
+devicePingLog.filter.keywordPlaceholder=璁惧鍙� / IP
+devicePingLog.overviewCount=鎬昏璁惧 {0} 鍙�
+devicePingLog.column.device=璁惧
+devicePingLog.column.status=鐘舵��
+devicePingLog.column.successRate=鎴愬姛鐜�
+devicePingLog.column.avg=骞冲潎
+devicePingLog.column.min=鏈�灏�
+devicePingLog.column.max=鏈�澶�
+devicePingLog.column.updateTime=鏇存柊鏃堕棿
+devicePingLog.column.message=璇存槑
+devicePingLog.column.action=鎿嶄綔
+devicePingLog.viewDetail=鏌ョ湅璇︽儏
+devicePingLog.detailTitle=璁惧璇︽儏
+devicePingLog.detailEmpty=浠庝笂鏂硅澶囨�昏閫夋嫨涓�鍙拌澶囨煡鐪嬬绾ф槑缁�
+devicePingLog.filter.timeRange=鏃堕棿鑼冨洿
+devicePingLog.filter.rangeSeparator=鑷�
+devicePingLog.filter.start=寮�濮�
+devicePingLog.filter.end=缁撴潫
+devicePingLog.quickRange.30m=30 鍒嗛挓
+devicePingLog.quickRange.1h=1 灏忔椂
+devicePingLog.quickRange.6h=6 灏忔椂
+devicePingLog.query=鏌ヨ
+devicePingLog.detail.status=鐘舵��
+devicePingLog.detail.packetSize=鍖呭ぇ灏�
+devicePingLog.detail.successRate=鎴愬姛鐜�
+devicePingLog.detail.avg=骞冲潎
+devicePingLog.detail.min=鏈�灏�
+devicePingLog.detail.max=鏈�澶�
+devicePingLog.chart.latencyTitle=寤惰繜
+devicePingLog.chart.empty=褰撳墠鑼冨洿鏆傛棤绉掔骇鏍锋湰
+devicePingLog.chart.availabilityTitle=鎴愬姛鐜� / 澶辫触娆℃暟
+devicePingLog.alertColumn.time=鏃堕棿
+devicePingLog.alertColumn.status=鐘舵��
+devicePingLog.alertColumn.message=璇存槑
+devicePingLog.samplingConfigText=閲囨牱 {0} ms / 瓒呮椂 {1} ms / 姣忔牱鏈� {2} 娆�
+devicePingLog.optionsLoadFailed=璁惧閰嶇疆鍔犺浇澶辫触
+devicePingLog.overviewLoadFailed=鎬昏鏁版嵁鍔犺浇澶辫触
+devicePingLog.selectTimeRange=璇烽�夋嫨鏃堕棿鑼冨洿
+devicePingLog.invalidDevice=璁惧淇℃伅鏃犳晥
+devicePingLog.detailLoadFailed=璁惧璇︽儏鍔犺浇澶辫触
+devicePingLog.chart.latency.avg=骞冲潎
+devicePingLog.chart.latency.min=鏈�灏�
+devicePingLog.chart.latency.max=鏈�澶�
+devicePingLog.chart.availability.failAxis=澶辫触
+devicePingLog.chart.availability.successRateAxis=鎴愬姛鐜�%
+devicePingLog.chart.availability.failCount=澶辫触娆℃暟
+devicePingLog.chart.availability.successRate=鎴愬姛鐜�
+devicePingLog.systemDefaultPacketSize=绯荤粺榛樿
+devicePingLog.status.ok=姝e父
+devicePingLog.status.unstable=娉㈠姩
+devicePingLog.status.timeout=瓒呮椂
+devicePingLog.status.error=寮傚父
+devicePingLog.status.noData=鏆傛棤鏁版嵁
 llm.logsTitle=LLM璋冪敤鏃ュ織
 llm.logDetailTitle=鏃ュ織璇︽儏
 llm.logDetailPrefix=鏃ュ織璇︽儏 - 
diff --git a/src/main/webapp/static/js/dashboard/dashboard.js b/src/main/webapp/static/js/dashboard/dashboard.js
index db6676a..42e63df 100644
--- a/src/main/webapp/static/js/dashboard/dashboard.js
+++ b/src/main/webapp/static/js/dashboard/dashboard.js
@@ -11,6 +11,7 @@
     el: "#app",
     data: function () {
       return {
+        rawPayload: null,
         loading: true,
         refreshing: false,
         countdown: REFRESH_SECONDS,
@@ -95,6 +96,12 @@
     },
     mounted: function () {
       var self = this;
+      this.refreshDocumentTitle();
+      if (window.WCS_I18N && typeof window.WCS_I18N.onReady === "function") {
+        window.WCS_I18N.onReady(function () {
+          self.refreshLocalizedState();
+        });
+      }
       this.$nextTick(function () {
         self.initCharts();
         self.loadDashboard(false);
@@ -108,6 +115,48 @@
       this.disposeCharts();
     },
     methods: {
+      formatMessage: function (text, params) {
+        var list = Array.isArray(params) ? params : [params];
+        return String(text == null ? "" : text).replace(/\{(\d+)\}/g, function (match, index) {
+          return list[index] == null ? "" : list[index];
+        });
+      },
+      i18n: function (key, fallback, params) {
+        if (window.WCS_I18N && typeof window.WCS_I18N.t === "function") {
+          var translated = window.WCS_I18N.t(key, params);
+          if (translated && translated !== key) {
+            return translated;
+          }
+        }
+        return this.formatMessage(fallback || key, params);
+      },
+      translateLegacyText: function (text) {
+        if (text == null || text === "") {
+          return text;
+        }
+        if (window.WCS_I18N && typeof window.WCS_I18N.tl === "function") {
+          return window.WCS_I18N.tl(String(text));
+        }
+        return text;
+      },
+      getCurrentLocale: function () {
+        if (window.WCS_I18N && typeof window.WCS_I18N.getLocale === "function") {
+          return window.WCS_I18N.getLocale();
+        }
+        return "zh-CN";
+      },
+      refreshDocumentTitle: function () {
+        document.title = this.i18n("dashboard.title", "绯荤粺浠〃鐩�");
+      },
+      refreshLocalizedState: function () {
+        this.refreshDocumentTitle();
+        if (this.rawPayload) {
+          this.applyData(this.rawPayload);
+          return;
+        }
+        this.$forceUpdate();
+        this.updateCharts();
+      },
       loadDashboard: function (manual) {
         var self = this;
         if (this.refreshing) {
@@ -124,11 +173,11 @@
               self.countdown = REFRESH_SECONDS;
               return;
             }
-            self.$message.error((res && res.msg) || "浠〃鐩樻暟鎹姞杞藉け璐�");
+            self.$message.error((res && res.msg) || self.i18n("dashboard.loadFailed", "浠〃鐩樻暟鎹姞杞藉け璐�"));
           },
           error: function () {
             if (manual) {
-              self.$message.error("浠〃鐩樻暟鎹姞杞藉け璐ワ紝璇锋鏌ユ帴鍙g姸鎬�");
+              self.$message.error(self.i18n("dashboard.loadFailedDetail", "浠〃鐩樻暟鎹姞杞藉け璐ワ紝璇锋鏌ユ帴鍙g姸鎬�"));
             }
           },
           complete: function () {
@@ -138,12 +187,165 @@
         });
       },
       applyData: function (payload) {
-        this.overview = payload.overview || this.overview;
-        this.tasks = payload.tasks || this.tasks;
-        this.devices = payload.devices || this.devices;
-        this.network = payload.network || this.network;
-        this.ai = payload.ai || this.ai;
+        var tasks = payload && payload.tasks ? payload.tasks : {};
+        var devices = payload && payload.devices ? payload.devices : {};
+        var network = payload && payload.network ? payload.network : {};
+        var ai = payload && payload.ai ? payload.ai : {};
+
+        this.rawPayload = payload || {};
+        this.refreshDocumentTitle();
+        this.overview = payload && payload.overview ? payload.overview : this.overview;
+        this.tasks = {
+          overview: tasks.overview || this.tasks.overview,
+          directionStats: this.normalizeTaskDirectionMetrics(tasks.directionStats),
+          stageStats: this.normalizeTaskStageMetrics(tasks.stageStats),
+          statusStats: this.normalizeMetricList(tasks.statusStats),
+          recentTasks: this.normalizeRecentTasks(tasks.recentTasks)
+        };
+        this.devices = {
+          overview: devices.overview || this.devices.overview,
+          typeStats: this.normalizeDeviceTypeStats(devices.typeStats)
+        };
+        this.network = {
+          overview: network.overview || this.network.overview,
+          samplingConfig: network.samplingConfig || this.network.samplingConfig,
+          statusStats: this.normalizeNetworkStatusMetrics(network.statusStats),
+          focusDevices: this.normalizeFocusDevices(network.focusDevices)
+        };
+        this.ai = {
+          overview: ai.overview || this.ai.overview,
+          routeStats: this.normalizeAiRouteStats(ai.routeStats),
+          routeList: this.normalizeAiRouteList(ai.routeList)
+        };
         this.updateCharts();
+      },
+      normalizeTaskDirectionMetrics: function (list) {
+        var self = this;
+        return cloneMetricList(list).map(function (item) {
+          var result = Object.assign({}, item);
+          if (item && item.name === "鍏ュ簱浠诲姟") {
+            result.name = self.i18n("dashboard.taskDirectionInbound", "鍏ュ簱浠诲姟");
+          } else if (item && item.name === "鍑哄簱浠诲姟") {
+            result.name = self.i18n("dashboard.taskDirectionOutbound", "鍑哄簱浠诲姟");
+          } else if (item && item.name === "绉诲簱浠诲姟") {
+            result.name = self.i18n("dashboard.taskDirectionMove", "绉诲簱浠诲姟");
+          } else {
+            result.name = self.translateLegacyText(item && item.name);
+          }
+          return result;
+        });
+      },
+      normalizeTaskStageMetrics: function (list) {
+        var self = this;
+        return cloneMetricList(list).map(function (item) {
+          var result = Object.assign({}, item);
+          if (item && item.name === "鎵ц涓�") {
+            result.name = self.i18n("dashboard.taskRunningLabel", "鎵ц涓�");
+          } else if (item && item.name === "寰呬汉宸�") {
+            result.name = self.i18n("dashboard.taskManualLabel", "寰呬汉宸�");
+          } else if (item && item.name === "宸插畬鎴�") {
+            result.name = self.i18n("dashboard.taskCompletedLabel", "宸插畬鎴�");
+          } else if (item && item.name === "鏂板缓") {
+            result.name = self.i18n("dashboard.taskNewLabel", "鏂板缓");
+          } else {
+            result.name = self.translateLegacyText(item && item.name);
+          }
+          return result;
+        });
+      },
+      normalizeMetricList: function (list) {
+        var self = this;
+        return cloneMetricList(list).map(function (item) {
+          var result = Object.assign({}, item);
+          result.name = self.translateLegacyText(item && item.name);
+          return result;
+        });
+      },
+      normalizeRecentTasks: function (list) {
+        var self = this;
+        return cloneMetricList(list).map(function (item) {
+          var result = Object.assign({}, item);
+          result.taskType = self.translateLegacyText(item && item.taskType);
+          result.status = self.translateLegacyText(item && item.status);
+          result.source = self.translateLegacyText(item && item.source);
+          result.target = self.translateLegacyText(item && item.target);
+          result.device = self.translateLegacyText(item && item.device);
+          return result;
+        });
+      },
+      normalizeAiRouteStats: function (list) {
+        var self = this;
+        return cloneMetricList(list).map(function (item) {
+          var result = Object.assign({}, item);
+          if (item && item.name === "鍙敤") {
+            result.name = self.i18n("dashboard.aiRouteStatusAvailable", "鍙敤");
+          } else if (item && item.name === "鍐峰嵈涓�") {
+            result.name = self.i18n("dashboard.aiRouteStatusCooling", "鍐峰嵈涓�");
+          } else if (item && item.name === "宸茬鐢�") {
+            result.name = self.i18n("dashboard.aiRouteStatusDisabled", "宸茬鐢�");
+          } else {
+            result.name = self.translateLegacyText(item && item.name);
+          }
+          return result;
+        });
+      },
+      normalizeNetworkStatusMetrics: function (list) {
+        var self = this;
+        return cloneMetricList(list).map(function (item) {
+          var result = Object.assign({}, item);
+          if (item && item.name === "姝e父") {
+            result.name = self.i18n("dashboard.networkOkLabel", "姝e父");
+          } else if (item && item.name === "娉㈠姩") {
+            result.name = self.i18n("dashboard.networkUnstableLabel", "娉㈠姩");
+          } else if (item && item.name === "瓒呮椂/寮傚父") {
+            result.name = self.i18n("dashboard.networkOfflineLabel", "瓒呮椂/寮傚父");
+          } else if (item && item.name === "鏆傛棤鏁版嵁") {
+            result.name = self.i18n("dashboard.networkNoDataLabel", "鏆傛棤鏁版嵁");
+          } else {
+            result.name = self.translateLegacyText(item && item.name);
+          }
+          return result;
+        });
+      },
+      normalizeDeviceTypeStats: function (list) {
+        var self = this;
+        return cloneMetricList(list).map(function (item) {
+          var result = Object.assign({}, item);
+          result.name = self.translateLegacyText(item && item.name);
+          return result;
+        });
+      },
+      normalizeAiRouteList: function (list) {
+        var self = this;
+        return cloneMetricList(list).map(function (item) {
+          var result = Object.assign({}, item);
+          if (result.statusType === "success") {
+            result.statusText = self.i18n("dashboard.aiRouteStatusAvailable", "鍙敤");
+          } else if (result.statusType === "warning") {
+            result.statusText = self.i18n("dashboard.aiRouteStatusCooling", "鍐峰嵈涓�");
+          } else {
+            result.statusText = self.i18n("dashboard.aiRouteStatusDisabled", "宸茬鐢�");
+          }
+          result.lastError = self.translateLegacyText(item && item.lastError);
+          return result;
+        });
+      },
+      normalizeFocusDevices: function (list) {
+        var self = this;
+        return cloneMetricList(list).map(function (item) {
+          var result = Object.assign({}, item);
+          if (result.statusType === "success") {
+            result.statusText = self.i18n("dashboard.networkOkLabel", "姝e父");
+          } else if (result.statusType === "warning") {
+            result.statusText = self.i18n("dashboard.networkUnstableLabel", "娉㈠姩");
+          } else if (result.statusType === "danger") {
+            result.statusText = self.i18n("dashboard.networkOfflineLabel", "瓒呮椂/寮傚父");
+          } else {
+            result.statusText = self.i18n("dashboard.networkNoDataLabel", "鏆傛棤鏁版嵁");
+          }
+          result.message = self.translateLegacyText(item && item.message);
+          return result;
+        });
       },
       initCharts: function () {
         this.disposeCharts();
@@ -247,6 +449,8 @@
       },
       buildDeviceTypeOption: function () {
         var data = cloneMetricList(this.devices.typeStats);
+        var onlineText = this.i18n("dashboard.deviceOnlineLegend", "鍦ㄧ嚎");
+        var offlineText = this.i18n("dashboard.deviceOfflineLegend", "绂荤嚎");
         return {
           color: ["#2fa38e", "#d8e2ec"],
           legend: {
@@ -255,7 +459,7 @@
             itemWidth: 10,
             itemHeight: 10,
             textStyle: { color: "#60778d", fontSize: 12 },
-            data: ["鍦ㄧ嚎", "绂荤嚎"]
+            data: [onlineText, offlineText]
           },
           grid: {
             left: 76,
@@ -282,7 +486,7 @@
             axisLabel: { color: "#60778d", fontSize: 12 }
           },
           series: [{
-            name: "鍦ㄧ嚎",
+            name: onlineText,
             type: "bar",
             stack: "device",
             barWidth: 18,
@@ -291,7 +495,7 @@
               borderRadius: [9, 0, 0, 9]
             }
           }, {
-            name: "绂荤嚎",
+            name: offlineText,
             type: "bar",
             stack: "device",
             barWidth: 18,
@@ -325,7 +529,7 @@
             left: "center",
             top: "55%",
             style: {
-              text: "鍙敤璺敱",
+              text: this.i18n("dashboard.chartCenter.availableRoutes", "鍙敤璺敱"),
               fill: "#7c8fa4",
               fontSize: 12
             }
@@ -370,7 +574,7 @@
             left: "center",
             top: "54%",
             style: {
-              text: "闇�鍏虫敞璁惧",
+              text: this.i18n("dashboard.chartCenter.attentionDevices", "闇�鍏虫敞璁惧"),
               fill: "#7c8fa4",
               fontSize: 12
             }
@@ -397,7 +601,7 @@
         if (!isFinite(num)) {
           return "0";
         }
-        return num.toLocaleString("zh-CN");
+        return num.toLocaleString(this.getCurrentLocale());
       },
       formatLatency: function (value) {
         var num;
@@ -408,22 +612,33 @@
         if (!isFinite(num)) {
           return "--";
         }
-        return num.toLocaleString("zh-CN", { maximumFractionDigits: 2 }) + " ms";
+        return num.toLocaleString(this.getCurrentLocale(), { maximumFractionDigits: 2 }) + " ms";
+      },
+      formatPercentValue: function (value) {
+        var num = Number(value);
+        if (!isFinite(num)) {
+          return "--";
+        }
+        return num.toLocaleString(this.getCurrentLocale(), { maximumFractionDigits: 2 }) + "%";
       },
       formatPacketSize: function (value) {
         var num = Number(value);
         if (!isFinite(num) || num < 0) {
-          return "绯荤粺榛樿";
+          return this.i18n("dashboard.systemDefaultPacketSize", "绯荤粺榛樿");
         }
-        return num + " B";
+        return num.toLocaleString(this.getCurrentLocale()) + " B";
       },
       displayText: function (value, fallback) {
         return value == null || value === "" ? (fallback || "") : value;
       },
       networkSamplingText: function () {
         var config = this.network && this.network.samplingConfig ? this.network.samplingConfig : {};
-        return "閲囨牱 " + this.displayText(config.intervalMs, 0) + " ms / 瓒呮椂 " + this.displayText(config.timeoutMs, 0) +
-          " ms / 姣忔牱鏈� " + this.displayText(config.probeCount, 0) + " 娆� / 鍖呭ぇ灏� " + this.formatPacketSize(config.packetSize);
+        return this.i18n("dashboard.networkSampling", "閲囨牱 {0} ms / 瓒呮椂 {1} ms / 姣忔牱鏈� {2} 娆� / 鍖呭ぇ灏� {3}", [
+          this.displayText(config.intervalMs, 0),
+          this.displayText(config.timeoutMs, 0),
+          this.displayText(config.probeCount, 0),
+          this.formatPacketSize(config.packetSize)
+        ]);
       },
       startAutoRefresh: function () {
         var self = this;
@@ -549,10 +764,19 @@
         window.open(targetMenu && targetMenu.url ? targetMenu.url : this.resolveAbsoluteViewPath(targetPath), "_blank");
       },
       openMonitor: function () {
-        this.openParentMenuView("/views/watch/console.html", "鐩戞帶鐢婚潰", "鐩戞帶鐢婚潰", "鐩戞帶绯荤粺");
+        this.openParentMenuView(
+          "/views/watch/console.html",
+          this.i18n("dashboard.monitorView", "鐩戞帶鐢婚潰"),
+          this.translateLegacyText("鐩戞帶鐢婚潰"),
+          this.translateLegacyText("鐩戞帶绯荤粺")
+        );
       },
       openDevicePingAnalysis: function () {
-        this.openParentMenuView("/views/devicePingLog/devicePingLog.html", "璁惧缃戠粶鍒嗘瀽", "璁惧缃戠粶鍒嗘瀽");
+        this.openParentMenuView(
+          "/views/devicePingLog/devicePingLog.html",
+          this.i18n("devicePingLog.title", "璁惧缃戠粶鍒嗘瀽"),
+          this.translateLegacyText("璁惧缃戠粶鍒嗘瀽")
+        );
       }
     }
   });
diff --git a/src/main/webapp/static/js/devicePingLog/devicePingLog.js b/src/main/webapp/static/js/devicePingLog/devicePingLog.js
index 5e002d9..60b73c3 100644
--- a/src/main/webapp/static/js/devicePingLog/devicePingLog.js
+++ b/src/main/webapp/static/js/devicePingLog/devicePingLog.js
@@ -49,6 +49,9 @@
     el: "#app",
     data: function () {
       return {
+        lastOptionsData: null,
+        lastOverviewData: null,
+        lastTrendData: null,
         overviewLoading: false,
         detailLoading: false,
         devices: [],
@@ -111,11 +114,21 @@
       },
       samplingConfigText: function () {
         var config = this.samplingConfig || createEmptySamplingConfig();
-        return "閲囨牱 " + config.intervalMs + " ms / 瓒呮椂 " + config.timeoutMs + " ms / 姣忔牱鏈� " + config.probeCount + " 娆�";
+        return this.i18n("devicePingLog.samplingConfigText", "閲囨牱 {0} ms / 瓒呮椂 {1} ms / 姣忔牱鏈� {2} 娆�", [
+          this.formatNumber(config.intervalMs),
+          this.formatNumber(config.timeoutMs),
+          this.formatNumber(config.probeCount)
+        ]);
       }
     },
     mounted: function () {
       var self = this;
+      this.refreshDocumentTitle();
+      if (window.WCS_I18N && typeof window.WCS_I18N.onReady === "function") {
+        window.WCS_I18N.onReady(function () {
+          self.refreshLocalizedState();
+        });
+      }
       this.$nextTick(function () {
         self.loadOptions();
         self.loadOverview();
@@ -132,6 +145,126 @@
       this.disposeCharts();
     },
     methods: {
+      formatMessage: function (text, params) {
+        var list = Array.isArray(params) ? params : [params];
+        return String(text == null ? "" : text).replace(/\{(\d+)\}/g, function (match, index) {
+          return list[index] == null ? "" : list[index];
+        });
+      },
+      i18n: function (key, fallback, params) {
+        if (window.WCS_I18N && typeof window.WCS_I18N.t === "function") {
+          var translated = window.WCS_I18N.t(key, params);
+          if (translated && translated !== key) {
+            return translated;
+          }
+        }
+        return this.formatMessage(fallback || key, params);
+      },
+      translateLegacyText: function (text) {
+        if (text == null || text === "") {
+          return text;
+        }
+        if (window.WCS_I18N && typeof window.WCS_I18N.tl === "function") {
+          return window.WCS_I18N.tl(String(text));
+        }
+        return text;
+      },
+      getCurrentLocale: function () {
+        if (window.WCS_I18N && typeof window.WCS_I18N.getLocale === "function") {
+          return window.WCS_I18N.getLocale();
+        }
+        return "zh-CN";
+      },
+      refreshDocumentTitle: function () {
+        document.title = this.i18n("devicePingLog.title", "璁惧缃戠粶鍒嗘瀽");
+      },
+      refreshLocalizedState: function () {
+        this.refreshDocumentTitle();
+        if (this.lastOptionsData) {
+          this.applyOptionsData(this.lastOptionsData);
+        }
+        if (this.lastOverviewData) {
+          this.applyOverviewData(this.lastOverviewData);
+        }
+        if (this.lastTrendData) {
+          this.applyTrendData(this.lastTrendData);
+        } else {
+          this.$forceUpdate();
+          this.updateCharts();
+        }
+      },
+      resolveStatusText: function (status) {
+        var code = status == null ? "" : String(status).toUpperCase();
+        if (code === "OK") {
+          return this.i18n("devicePingLog.status.ok", "姝e父");
+        }
+        if (code === "UNSTABLE") {
+          return this.i18n("devicePingLog.status.unstable", "娉㈠姩");
+        }
+        if (code === "TIMEOUT") {
+          return this.i18n("devicePingLog.status.timeout", "瓒呮椂");
+        }
+        if (code === "ERROR") {
+          return this.i18n("devicePingLog.status.error", "寮傚父");
+        }
+        if (code === "NO_DATA") {
+          return this.i18n("devicePingLog.status.noData", "鏆傛棤鏁版嵁");
+        }
+        if (!code) {
+          return "--";
+        }
+        return this.translateLegacyText(status);
+      },
+      applyOptionsData: function (data) {
+        this.lastOptionsData = data || {};
+        this.refreshDocumentTitle();
+        this.devices = (data && data.devices) || [];
+        this.availableDays = (data && data.days) || [];
+        this.samplingConfig = Object.assign(createEmptySamplingConfig(), data && data.samplingConfig ? data.samplingConfig : {});
+      },
+      applyOverviewData: function (data) {
+        this.lastOverviewData = data || {};
+        this.overviewSummary = Object.assign(createEmptyOverviewSummary(), data && data.summary ? data.summary : {});
+        this.overviewRows = this.normalizeOverviewRows(data && data.devices ? data.devices : []);
+      },
+      applyTrendData: function (data) {
+        this.lastTrendData = data || {};
+        this.detailSummary = this.normalizeDetailSummary(data && data.summary ? data.summary : {});
+        this.series = (data && data.series) || [];
+        this.alerts = this.normalizeAlerts(data && data.alerts ? data.alerts : []);
+        this.updateCharts();
+      },
+      normalizeOverviewRows: function (rows) {
+        var self = this;
+        return (rows || []).map(function (item) {
+          var result = Object.assign({}, item);
+          result.statusText = self.resolveStatusText(item && item.status);
+          result.message = self.translateLegacyText(item && item.message);
+          result.label = self.translateLegacyText(item && item.label);
+          return result;
+        });
+      },
+      normalizeDetailSummary: function (summary) {
+        var result = Object.assign(createEmptyDetailSummary(), summary || {});
+        result.latestStatus = this.resolveStatusText(result.latestStatus);
+        return result;
+      },
+      normalizeAlerts: function (alerts) {
+        var self = this;
+        return (alerts || []).map(function (item) {
+          var result = Object.assign({}, item);
+          result.status = self.resolveStatusText(item && item.status);
+          result.message = self.translateLegacyText(item && item.message);
+          return result;
+        });
+      },
+      formatNumber: function (value) {
+        var num = Number(value || 0);
+        if (!isFinite(num)) {
+          return "0";
+        }
+        return num.toLocaleString(this.getCurrentLocale());
+      },
       loadOptions: function () {
         var self = this;
         $.ajax({
@@ -141,15 +274,13 @@
           success: function (res) {
             if (res && res.code === 200) {
               var data = res.data || {};
-              self.devices = data.devices || [];
-              self.availableDays = data.days || [];
-              self.samplingConfig = Object.assign(createEmptySamplingConfig(), data.samplingConfig || {});
+              self.applyOptionsData(data);
               return;
             }
-            self.$message.error((res && res.msg) || "璁惧閰嶇疆鍔犺浇澶辫触");
+            self.$message.error((res && res.msg) || self.i18n("devicePingLog.optionsLoadFailed", "璁惧閰嶇疆鍔犺浇澶辫触"));
           },
           error: function () {
-            self.$message.error("璁惧閰嶇疆鍔犺浇澶辫触");
+            self.$message.error(self.i18n("devicePingLog.optionsLoadFailed", "璁惧閰嶇疆鍔犺浇澶辫触"));
           }
         });
       },
@@ -162,19 +293,17 @@
           method: "GET",
           success: function (res) {
             if (res && res.code === 200) {
-              var data = res.data || {};
-              self.overviewSummary = Object.assign(createEmptyOverviewSummary(), data.summary || {});
-              self.overviewRows = data.devices || [];
+              self.applyOverviewData(res.data || {});
               return;
             }
             self.overviewSummary = createEmptyOverviewSummary();
             self.overviewRows = [];
-            self.$message.error((res && res.msg) || "鎬昏鏁版嵁鍔犺浇澶辫触");
+            self.$message.error((res && res.msg) || self.i18n("devicePingLog.overviewLoadFailed", "鎬昏鏁版嵁鍔犺浇澶辫触"));
           },
           error: function () {
             self.overviewSummary = createEmptyOverviewSummary();
             self.overviewRows = [];
-            self.$message.error("鎬昏鏁版嵁鍔犺浇澶辫触");
+            self.$message.error(self.i18n("devicePingLog.overviewLoadFailed", "鎬昏鏁版嵁鍔犺浇澶辫触"));
           },
           complete: function () {
             self.overviewLoading = false;
@@ -204,12 +333,12 @@
           return;
         }
         if (!this.detailFilters.range || this.detailFilters.range.length !== 2) {
-          this.$message.warning("璇烽�夋嫨鏃堕棿鑼冨洿");
+          this.$message.warning(this.i18n("devicePingLog.selectTimeRange", "璇烽�夋嫨鏃堕棿鑼冨洿"));
           return;
         }
         var parts = this.detailFilters.deviceKey.split("#");
         if (parts.length !== 2) {
-          this.$message.warning("璁惧淇℃伅鏃犳晥");
+          this.$message.warning(this.i18n("devicePingLog.invalidDevice", "璁惧淇℃伅鏃犳晥"));
           return;
         }
         var self = this;
@@ -226,25 +355,21 @@
           },
           success: function (res) {
             if (res && res.code === 200) {
-              var data = res.data || {};
-              self.detailSummary = Object.assign(createEmptyDetailSummary(), data.summary || {});
-              self.series = data.series || [];
-              self.alerts = data.alerts || [];
-              self.updateCharts();
+              self.applyTrendData(res.data || {});
               return;
             }
             self.detailSummary = createEmptyDetailSummary();
             self.series = [];
             self.alerts = [];
             self.updateCharts();
-            self.$message.error((res && res.msg) || "璁惧璇︽儏鍔犺浇澶辫触");
+            self.$message.error((res && res.msg) || self.i18n("devicePingLog.detailLoadFailed", "璁惧璇︽儏鍔犺浇澶辫触"));
           },
           error: function () {
             self.detailSummary = createEmptyDetailSummary();
             self.series = [];
             self.alerts = [];
             self.updateCharts();
-            self.$message.error("璁惧璇︽儏鍔犺浇澶辫触");
+            self.$message.error(self.i18n("devicePingLog.detailLoadFailed", "璁惧璇︽儏鍔犺浇澶辫触"));
           },
           complete: function () {
             self.detailLoading = false;
@@ -336,21 +461,21 @@
             bottom: 10
           }],
           series: [{
-            name: "骞冲潎",
+            name: this.i18n("devicePingLog.chart.latency.avg", "骞冲潎"),
             type: "line",
             showSymbol: false,
             sampling: "lttb",
             data: this.series.map(function (item) { return item.avgLatencyMs; }),
             lineStyle: { width: 2 }
           }, {
-            name: "鏈�灏�",
+            name: this.i18n("devicePingLog.chart.latency.min", "鏈�灏�"),
             type: "line",
             showSymbol: false,
             sampling: "lttb",
             data: this.series.map(function (item) { return item.minLatencyMs; }),
             lineStyle: { width: 1.5 }
           }, {
-            name: "鏈�澶�",
+            name: this.i18n("devicePingLog.chart.latency.max", "鏈�澶�"),
             type: "line",
             showSymbol: false,
             sampling: "lttb",
@@ -389,14 +514,14 @@
           },
           yAxis: [{
             type: "value",
-            name: "澶辫触",
+            name: this.i18n("devicePingLog.chart.availability.failAxis", "澶辫触"),
             splitLine: { lineStyle: { color: "#edf2f7" } },
             axisLine: { show: false },
             axisTick: { show: false },
             axisLabel: { color: "#7f92a5" }
           }, {
             type: "value",
-            name: "鎴愬姛鐜�%",
+            name: this.i18n("devicePingLog.chart.availability.successRateAxis", "鎴愬姛鐜�%"),
             min: 0,
             max: 100,
             splitLine: { show: false },
@@ -412,7 +537,7 @@
             bottom: 10
           }],
           series: [{
-            name: "澶辫触娆℃暟",
+            name: this.i18n("devicePingLog.chart.availability.failCount", "澶辫触娆℃暟"),
             type: "bar",
             yAxisIndex: 0,
             barMaxWidth: 18,
@@ -421,7 +546,7 @@
               borderRadius: [6, 6, 0, 0]
             }
           }, {
-            name: "鎴愬姛鐜�",
+            name: this.i18n("devicePingLog.chart.availability.successRate", "鎴愬姛鐜�"),
             type: "line",
             yAxisIndex: 1,
             showSymbol: false,
@@ -444,22 +569,33 @@
         return "offline";
       },
       formatLatency: function (value) {
+        var num;
         if (value === null || value === undefined || value === "") {
           return "--";
         }
-        return value + " ms";
+        num = Number(value);
+        if (!isFinite(num)) {
+          return "--";
+        }
+        return num.toLocaleString(this.getCurrentLocale(), { maximumFractionDigits: 2 }) + " ms";
       },
       formatPercent: function (value) {
+        var num;
         if (value === null || value === undefined || value === "") {
           return "--";
         }
-        return value + "%";
+        num = Number(value);
+        if (!isFinite(num)) {
+          return "--";
+        }
+        return num.toLocaleString(this.getCurrentLocale(), { maximumFractionDigits: 2 }) + "%";
       },
       formatPacketSize: function (value) {
-        if (value === null || value === undefined || value === "" || value < 0) {
-          return "绯荤粺榛樿";
+        var num = Number(value);
+        if (!isFinite(num) || num < 0) {
+          return this.i18n("devicePingLog.systemDefaultPacketSize", "绯荤粺榛樿");
         }
-        return value + " B";
+        return num.toLocaleString(this.getCurrentLocale()) + " B";
       }
     }
   });
diff --git a/src/main/webapp/views/dashboard/dashboard.html b/src/main/webapp/views/dashboard/dashboard.html
index 3c2b87e..1a806a2 100644
--- a/src/main/webapp/views/dashboard/dashboard.html
+++ b/src/main/webapp/views/dashboard/dashboard.html
@@ -713,63 +713,63 @@
     <div class="hero-main">
       <div class="hero-copy">
         <div class="hero-eyebrow">WCS Dashboard</div>
-        <h1 class="hero-title">绯荤粺浠〃鐩�</h1>
+        <h1 class="hero-title">{{ i18n('dashboard.title', '绯荤粺浠〃鐩�') }}</h1>
       </div>
       <div class="hero-actions">
-        <el-button size="small" plain @click="openMonitor">鎵撳紑鐩戞帶鐢婚潰</el-button>
-        <el-button size="small" type="primary" :loading="refreshing" @click="loadDashboard(true)">绔嬪嵆鍒锋柊</el-button>
+        <el-button size="small" plain @click="openMonitor">{{ i18n('dashboard.openMonitor', '鎵撳紑鐩戞帶鐢婚潰') }}</el-button>
+        <el-button size="small" type="primary" :loading="refreshing" @click="loadDashboard(true)">{{ i18n('dashboard.refreshNow', '绔嬪嵆鍒锋柊') }}</el-button>
       </div>
     </div>
 
     <div class="hero-stat-grid">
       <div class="hero-stat-row">
         <div class="hero-row-head">
-          <div class="hero-row-kicker">鐘舵�佹瑙�</div>
-          <div class="hero-row-note">绯荤粺涓庡埛鏂拌妭濂�</div>
+          <div class="hero-row-kicker">{{ i18n('dashboard.overviewKicker', '鐘舵�佹瑙�') }}</div>
+          <div class="hero-row-note">{{ i18n('dashboard.overviewNote', '绯荤粺涓庡埛鏂拌妭濂�') }}</div>
         </div>
         <div class="hero-status-grid">
           <div class="hero-meta">
-            <div class="hero-meta-label">绯荤粺鐘舵��</div>
-            <div class="hero-meta-value">{{ overview.systemRunning ? '杩愯涓�' : '宸叉殏鍋�' }}</div>
-            <div class="hero-meta-desc">WCS 涓绘湇鍔″綋鍓嶇姸鎬�</div>
+            <div class="hero-meta-label">{{ i18n('dashboard.systemStatusLabel', '绯荤粺鐘舵��') }}</div>
+            <div class="hero-meta-value">{{ overview.systemRunning ? i18n('dashboard.systemRunning', '杩愯涓�') : i18n('dashboard.systemPaused', '宸叉殏鍋�') }}</div>
+            <div class="hero-meta-desc">{{ i18n('dashboard.systemStatusDesc', 'WCS 涓绘湇鍔″綋鍓嶇姸鎬�') }}</div>
           </div>
           <div class="hero-meta">
-            <div class="hero-meta-label">鏈�杩戝埛鏂�</div>
+            <div class="hero-meta-label">{{ i18n('dashboard.lastRefreshLabel', '鏈�杩戝埛鏂�') }}</div>
             <div class="hero-meta-value">{{ displayText(overview.generatedAt, '-') }}</div>
-            <div class="hero-meta-desc">鏈�杩戜竴娆¤仛鍚堟暟鎹敓鎴愭椂闂�</div>
+            <div class="hero-meta-desc">{{ i18n('dashboard.lastRefreshDesc', '鏈�杩戜竴娆¤仛鍚堟暟鎹敓鎴愭椂闂�') }}</div>
           </div>
           <div class="hero-meta">
-            <div class="hero-meta-label">鑷姩鍒锋柊</div>
-            <div class="hero-meta-value">{{ countdown }}s 鍚庡埛鏂�</div>
-            <div class="hero-meta-desc">椤甸潰鑷姩鏇存柊鍊掕鏃�</div>
+            <div class="hero-meta-label">{{ i18n('dashboard.autoRefreshLabel', '鑷姩鍒锋柊') }}</div>
+            <div class="hero-meta-value">{{ i18n('dashboard.autoRefreshValue', '{0}s 鍚庡埛鏂�', [countdown]) }}</div>
+            <div class="hero-meta-desc">{{ i18n('dashboard.autoRefreshDesc', '椤甸潰鑷姩鏇存柊鍊掕鏃�') }}</div>
           </div>
         </div>
       </div>
       <div class="hero-stat-row">
         <div class="hero-row-head">
-          <div class="hero-row-kicker">鏍稿績鎸囨爣</div>
-          <div class="hero-row-note">浠诲姟銆佽澶囦笌 AI 鎬昏</div>
+          <div class="hero-row-kicker">{{ i18n('dashboard.coreMetricsKicker', '鏍稿績鎸囨爣') }}</div>
+          <div class="hero-row-note">{{ i18n('dashboard.coreMetricsNote', '浠诲姟銆佽澶囦笌 AI 鎬昏') }}</div>
         </div>
         <div class="hero-metric-grid">
           <div class="summary-card">
-            <div class="label">浠诲姟鎬绘暟</div>
+            <div class="label">{{ i18n('dashboard.taskTotalLabel', '浠诲姟鎬绘暟') }}</div>
             <div class="value">{{ formatNumber(overview.taskTotal) }}</div>
-            <div class="desc">褰撳墠鎵ц涓� {{ formatNumber(overview.taskRunning) }}</div>
+            <div class="desc">{{ i18n('dashboard.taskTotalDesc', '褰撳墠鎵ц涓� {0}', [formatNumber(overview.taskRunning)]) }}</div>
           </div>
           <div class="summary-card">
-            <div class="label">鍦ㄧ嚎璁惧</div>
+            <div class="label">{{ i18n('dashboard.deviceOnlineLabel', '鍦ㄧ嚎璁惧') }}</div>
             <div class="value">{{ formatNumber(overview.deviceOnline) }}</div>
-            <div class="desc">鎬昏澶� {{ formatNumber(overview.deviceTotal) }}锛屽憡璀� {{ formatNumber(overview.deviceAlarm) }}</div>
+            <div class="desc">{{ i18n('dashboard.deviceOnlineDesc', '鎬昏澶� {0}锛屽憡璀� {1}', [formatNumber(overview.deviceTotal), formatNumber(overview.deviceAlarm)]) }}</div>
           </div>
           <div class="summary-card">
-            <div class="label">AI 绱 Tokens</div>
+            <div class="label">{{ i18n('dashboard.aiTokenTotalLabel', 'AI 绱 Tokens') }}</div>
             <div class="value">{{ formatNumber(overview.aiTokenTotal) }}</div>
-            <div class="desc">鎸� AI 浼氳瘽绱缁熻</div>
+            <div class="desc">{{ i18n('dashboard.aiTokenTotalDesc', '鎸� AI 浼氳瘽绱缁熻') }}</div>
           </div>
           <div class="summary-card">
-            <div class="label">LLM 璋冪敤娆℃暟</div>
+            <div class="label">{{ i18n('dashboard.aiCallTotalLabel', 'LLM 璋冪敤娆℃暟') }}</div>
             <div class="value">{{ formatNumber(overview.aiCallTotal) }}</div>
-            <div class="desc">鏈�杩戜竴杞繍琛屾儏鍐靛凡绾冲叆涓嬫柟 AI 鍖哄煙</div>
+            <div class="desc">{{ i18n('dashboard.aiCallTotalDesc', '鏈�杩戜竴杞繍琛屾儏鍐靛凡绾冲叆涓嬫柟 AI 鍖哄煙') }}</div>
           </div>
         </div>
       </div>
@@ -782,41 +782,41 @@
         <div class="panel-header">
           <div>
             <div class="panel-kicker">Task</div>
-            <h2 class="panel-title">浠诲姟鎬佸娍</h2>
-            <div class="panel-desc">浠庝换鍔$被鍨嬨�佹墽琛岄樁娈靛拰鏈�杩戞祦杞褰曞揩閫熷垽鏂綋鍓嶄綔涓氬帇鍔涖��</div>
+            <h2 class="panel-title">{{ i18n('dashboard.taskPanelTitle', '浠诲姟鎬佸娍') }}</h2>
+            <div class="panel-desc">{{ i18n('dashboard.taskPanelDesc', '浠庝换鍔$被鍨嬨�佹墽琛岄樁娈靛拰鏈�杩戞祦杞褰曞揩閫熷垽鏂綋鍓嶄綔涓氬帇鍔涖��') }}</div>
           </div>
         </div>
 
         <div class="mini-grid">
           <div class="mini-card task-mini-running">
-            <div class="mini-label">鎵ц涓�</div>
+            <div class="mini-label">{{ i18n('dashboard.taskRunningLabel', '鎵ц涓�') }}</div>
             <div class="mini-value">{{ formatNumber(tasks.overview.running) }}</div>
-            <div class="mini-hint">褰撳墠姝e湪娴佽浆鐨勪换鍔�</div>
+            <div class="mini-hint">{{ i18n('dashboard.taskRunningHint', '褰撳墠姝e湪娴佽浆鐨勪换鍔�') }}</div>
           </div>
           <div class="mini-card task-mini-manual">
-            <div class="mini-label">寰呬汉宸�</div>
+            <div class="mini-label">{{ i18n('dashboard.taskManualLabel', '寰呬汉宸�') }}</div>
             <div class="mini-value">{{ formatNumber(tasks.overview.manual) }}</div>
-            <div class="mini-hint">闇�浜哄伐鍏虫敞鎴栧洖婊�</div>
+            <div class="mini-hint">{{ i18n('dashboard.taskManualHint', '闇�浜哄伐鍏虫敞鎴栧洖婊�') }}</div>
           </div>
           <div class="mini-card task-mini-completed">
-            <div class="mini-label">宸插畬鎴�</div>
+            <div class="mini-label">{{ i18n('dashboard.taskCompletedLabel', '宸插畬鎴�') }}</div>
             <div class="mini-value">{{ formatNumber(tasks.overview.completed) }}</div>
-            <div class="mini-hint">宸茬粡瀹屾垚鎴栬惤璐�</div>
+            <div class="mini-hint">{{ i18n('dashboard.taskCompletedHint', '宸茬粡瀹屾垚鎴栬惤璐�') }}</div>
           </div>
           <div class="mini-card task-mini-new">
-            <div class="mini-label">鏂板缓</div>
+            <div class="mini-label">{{ i18n('dashboard.taskNewLabel', '鏂板缓') }}</div>
             <div class="mini-value">{{ formatNumber(tasks.overview.newCreated) }}</div>
-            <div class="mini-hint">鍒氳繘鍏ヨ皟搴︽祦绋�</div>
+            <div class="mini-hint">{{ i18n('dashboard.taskNewHint', '鍒氳繘鍏ヨ皟搴︽祦绋�') }}</div>
           </div>
         </div>
 
         <div class="chart-grid">
           <div class="chart-card">
-            <div class="chart-title">浠诲姟绫诲瀷鍒嗗竷</div>
+            <div class="chart-title">{{ i18n('dashboard.taskDirectionChartTitle', '浠诲姟绫诲瀷鍒嗗竷') }}</div>
             <div ref="taskDirectionChart" class="chart-box"></div>
           </div>
           <div class="chart-card">
-            <div class="chart-title">浠诲姟闃舵姒傝</div>
+            <div class="chart-title">{{ i18n('dashboard.taskStageChartTitle', '浠诲姟闃舵姒傝') }}</div>
             <div ref="taskStageChart" class="chart-box"></div>
           </div>
         </div>
@@ -833,8 +833,8 @@
         <div class="panel-header">
           <div>
             <div class="panel-kicker">Recent</div>
-            <h2 class="panel-title">鏈�杩戜换鍔�</h2>
-            <div class="panel-desc">甯姪蹇�熷垽鏂换鍔℃槸鍚﹀爢绉�佹槸鍚﹁璁惧鎺ユ墜锛屼互鍙婃渶杩戠殑浠诲姟鐩爣浣嶇疆銆�</div>
+            <h2 class="panel-title">{{ i18n('dashboard.recentPanelTitle', '鏈�杩戜换鍔�') }}</h2>
+            <div class="panel-desc">{{ i18n('dashboard.recentPanelDesc', '甯姪蹇�熷垽鏂换鍔℃槸鍚﹀爢绉�佹槸鍚﹁璁惧鎺ユ墜锛屼互鍙婃渶杩戠殑浠诲姟鐩爣浣嶇疆銆�') }}</div>
           </div>
         </div>
 
@@ -844,15 +844,15 @@
               stripe
               size="mini"
               height="360"
-              empty-text="鏆傛棤浠诲姟璁板綍">
-            <el-table-column prop="wrkNo" label="浠诲姟鍙�" min-width="100"></el-table-column>
-            <el-table-column prop="taskType" label="浠诲姟绫诲瀷" min-width="110"></el-table-column>
-            <el-table-column prop="status" label="鐘舵��" min-width="160" show-overflow-tooltip></el-table-column>
-            <el-table-column prop="source" label="鏉ユ簮" min-width="170" show-overflow-tooltip></el-table-column>
-            <el-table-column prop="target" label="鐩爣" min-width="170" show-overflow-tooltip></el-table-column>
-            <el-table-column prop="device" label="鎵ц璁惧" min-width="180" show-overflow-tooltip></el-table-column>
-            <el-table-column prop="barcode" label="鏉$爜" min-width="150" show-overflow-tooltip></el-table-column>
-            <el-table-column prop="updateTime" label="鏈�杩戞洿鏂版椂闂�" min-width="170"></el-table-column>
+              :empty-text="i18n('dashboard.recentEmpty', '鏆傛棤浠诲姟璁板綍')">
+            <el-table-column prop="wrkNo" :label="i18n('dashboard.column.workNo', '浠诲姟鍙�')" min-width="100"></el-table-column>
+            <el-table-column prop="taskType" :label="i18n('dashboard.column.taskType', '浠诲姟绫诲瀷')" min-width="110"></el-table-column>
+            <el-table-column prop="status" :label="i18n('dashboard.column.status', '鐘舵��')" min-width="160" show-overflow-tooltip></el-table-column>
+            <el-table-column prop="source" :label="i18n('dashboard.column.source', '鏉ユ簮')" min-width="170" show-overflow-tooltip></el-table-column>
+            <el-table-column prop="target" :label="i18n('dashboard.column.target', '鐩爣')" min-width="170" show-overflow-tooltip></el-table-column>
+            <el-table-column prop="device" :label="i18n('dashboard.column.device', '鎵ц璁惧')" min-width="180" show-overflow-tooltip></el-table-column>
+            <el-table-column prop="barcode" :label="i18n('dashboard.column.barcode', '鏉$爜')" min-width="150" show-overflow-tooltip></el-table-column>
+            <el-table-column prop="updateTime" :label="i18n('dashboard.column.updateTime', '鏈�杩戞洿鏂版椂闂�')" min-width="170"></el-table-column>
           </el-table>
         </div>
       </section>
@@ -861,37 +861,37 @@
         <div class="panel-header">
           <div>
             <div class="panel-kicker">AI</div>
-            <h2 class="panel-title">AI 杩愯鎯呭喌</h2>
-            <div class="panel-desc">鏌ョ湅 AI 浼氳瘽绱 Tokens銆丩LM 璋冪敤閲忥紝浠ュ強璺敱鐨勫彲鐢ㄤ笌鍐峰嵈鐘舵�併��</div>
+            <h2 class="panel-title">{{ i18n('dashboard.aiPanelTitle', 'AI 杩愯鎯呭喌') }}</h2>
+            <div class="panel-desc">{{ i18n('dashboard.aiPanelDesc', '鏌ョ湅 AI 浼氳瘽绱 Tokens銆丩LM 璋冪敤閲忥紝浠ュ強璺敱鐨勫彲鐢ㄤ笌鍐峰嵈鐘舵�併��') }}</div>
           </div>
-          <el-tag size="small" type="success">鍙敤璺敱 {{ formatNumber(ai.overview.availableRouteCount) }}</el-tag>
+          <el-tag size="small" type="success">{{ i18n('dashboard.availableRoutesTag', '鍙敤璺敱 {0}', [formatNumber(ai.overview.availableRouteCount)]) }}</el-tag>
         </div>
 
         <div class="mini-grid">
           <div class="mini-card">
-            <div class="mini-label">绱 Tokens</div>
+            <div class="mini-label">{{ i18n('dashboard.aiTokenCardLabel', '绱 Tokens') }}</div>
             <div class="mini-value">{{ formatNumber(ai.overview.tokenTotal) }}</div>
-            <div class="mini-hint">Prompt + Completion</div>
+            <div class="mini-hint">{{ i18n('dashboard.aiTokenCardHint', 'Prompt + Completion') }}</div>
           </div>
           <div class="mini-card">
-            <div class="mini-label">鎻愰棶杞</div>
+            <div class="mini-label">{{ i18n('dashboard.aiAskCountLabel', '鎻愰棶杞') }}</div>
             <div class="mini-value">{{ formatNumber(ai.overview.askCount) }}</div>
-            <div class="mini-hint">AI 瀵硅瘽绱杞</div>
+            <div class="mini-hint">{{ i18n('dashboard.aiAskCountHint', 'AI 瀵硅瘽绱杞') }}</div>
           </div>
           <div class="mini-card">
-            <div class="mini-label">LLM 璋冪敤</div>
+            <div class="mini-label">{{ i18n('dashboard.aiLlmCallLabel', 'LLM 璋冪敤') }}</div>
             <div class="mini-value">{{ formatNumber(ai.overview.llmCallTotal) }}</div>
-            <div class="mini-hint">鎴愬姛 {{ formatNumber(ai.overview.successCallTotal) }} / 澶辫触 {{ formatNumber(ai.overview.failCallTotal) }}</div>
+            <div class="mini-hint">{{ i18n('dashboard.aiLlmCallHint', '鎴愬姛 {0} / 澶辫触 {1}', [formatNumber(ai.overview.successCallTotal), formatNumber(ai.overview.failCallTotal)]) }}</div>
           </div>
           <div class="mini-card">
-            <div class="mini-label">浼氳瘽鏁�</div>
+            <div class="mini-label">{{ i18n('dashboard.aiSessionCountLabel', '浼氳瘽鏁�') }}</div>
             <div class="mini-value">{{ formatNumber(ai.overview.sessionCount) }}</div>
-            <div class="mini-hint">鏈�杩戣皟鐢� {{ displayText(ai.overview.lastCallTime, '-') }}</div>
+            <div class="mini-hint">{{ i18n('dashboard.aiSessionCountHint', '鏈�杩戣皟鐢� {0}', [displayText(ai.overview.lastCallTime, '-')]) }}</div>
           </div>
         </div>
 
         <div class="chart-card">
-          <div class="chart-title">AI 璺敱鐘舵��</div>
+          <div class="chart-title">{{ i18n('dashboard.aiRouteChartTitle', 'AI 璺敱鐘舵��') }}</div>
           <div ref="aiRouteChart" class="ai-chart-box"></div>
         </div>
 
@@ -899,17 +899,17 @@
           <div v-for="route in ai.routeList.slice(0, 6)" :key="route.name + '-' + route.model + '-' + route.priority" class="route-row">
             <div class="route-row-main">
               <div class="route-row-name">{{ route.name }}</div>
-              <div class="route-row-desc">妯″瀷 {{ displayText(route.model, '-') }}锛屼紭鍏堢骇 {{ displayText(route.priority, '-') }}</div>
+              <div class="route-row-desc">{{ i18n('dashboard.aiRouteDesc', '妯″瀷 {0}锛屼紭鍏堢骇 {1}', [displayText(route.model, '-'), displayText(route.priority, '-')]) }}</div>
               <div v-if="route.lastError" class="route-error">{{ route.lastError }}</div>
             </div>
             <div class="route-row-side">
               <el-tag size="mini" :type="route.statusType">{{ route.statusText }}</el-tag>
-              <div class="route-extra">鎴愬姛 {{ formatNumber(route.successCount) }} / 澶辫触 {{ formatNumber(route.failCount) }}</div>
-              <div class="route-extra">鏈�杩戜娇鐢� {{ displayText(route.lastUsedTime, '-') }}</div>
+              <div class="route-extra">{{ i18n('dashboard.aiRouteResult', '鎴愬姛 {0} / 澶辫触 {1}', [formatNumber(route.successCount), formatNumber(route.failCount)]) }}</div>
+              <div class="route-extra">{{ i18n('dashboard.aiRouteLastUsed', '鏈�杩戜娇鐢� {0}', [displayText(route.lastUsedTime, '-')]) }}</div>
             </div>
           </div>
         </div>
-        <el-empty v-else description="鏆傛棤 AI 璺敱鏁版嵁"></el-empty>
+        <el-empty v-else :description="i18n('dashboard.aiRouteEmpty', '鏆傛棤 AI 璺敱鏁版嵁')"></el-empty>
       </section>
     </div>
 
@@ -918,37 +918,37 @@
         <div class="panel-header">
           <div>
             <div class="panel-kicker">Devices</div>
-            <h2 class="panel-title">璁惧鎬佸娍</h2>
-            <div class="panel-desc">姹囨�昏緭閫佺珯鐐广�佸爢鍨涙満銆佸弻宸ヤ綅鍫嗗灈鏈轰笌 RGV 鐨勫湪绾裤�佸繖纰屽拰鍛婅鎯呭喌銆�</div>
+            <h2 class="panel-title">{{ i18n('dashboard.devicePanelTitle', '璁惧鎬佸娍') }}</h2>
+            <div class="panel-desc">{{ i18n('dashboard.devicePanelDesc', '姹囨�昏緭閫佺珯鐐广�佸爢鍨涙満銆佸弻宸ヤ綅鍫嗗灈鏈轰笌 RGV 鐨勫湪绾裤�佸繖纰屽拰鍛婅鎯呭喌銆�') }}</div>
           </div>
-          <el-tag size="small" type="info">鍦ㄧ嚎鐜� {{ devices.overview.onlineRate || 0 }}%</el-tag>
+          <el-tag size="small" type="info">{{ i18n('dashboard.deviceOnlineRate', '鍦ㄧ嚎鐜� {0}', [formatPercentValue(devices.overview.onlineRate || 0)]) }}</el-tag>
         </div>
 
         <div class="mini-grid">
           <div class="mini-card">
-            <div class="mini-label">璁惧鎬绘暟</div>
+            <div class="mini-label">{{ i18n('dashboard.deviceTotalLabel', '璁惧鎬绘暟') }}</div>
             <div class="mini-value">{{ formatNumber(devices.overview.total) }}</div>
-            <div class="mini-hint">宸插惎鐢ㄩ厤缃澶�</div>
+            <div class="mini-hint">{{ i18n('dashboard.deviceTotalHint', '宸插惎鐢ㄩ厤缃澶�') }}</div>
           </div>
           <div class="mini-card">
-            <div class="mini-label">鍦ㄧ嚎璁惧</div>
+            <div class="mini-label">{{ i18n('dashboard.deviceOnlineCardLabel', '鍦ㄧ嚎璁惧') }}</div>
             <div class="mini-value">{{ formatNumber(devices.overview.online) }}</div>
-            <div class="mini-hint">瀹炴椂杩為�氳澶囨暟閲�</div>
+            <div class="mini-hint">{{ i18n('dashboard.deviceOnlineCardHint', '瀹炴椂杩為�氳澶囨暟閲�') }}</div>
           </div>
           <div class="mini-card">
-            <div class="mini-label">蹇欑璁惧</div>
+            <div class="mini-label">{{ i18n('dashboard.deviceBusyLabel', '蹇欑璁惧') }}</div>
             <div class="mini-value">{{ formatNumber(devices.overview.busy) }}</div>
-            <div class="mini-hint">褰撳墠鎵胯浇浠诲姟鐨勮澶�</div>
+            <div class="mini-hint">{{ i18n('dashboard.deviceBusyHint', '褰撳墠鎵胯浇浠诲姟鐨勮澶�') }}</div>
           </div>
           <div class="mini-card">
-            <div class="mini-label">鍛婅璁惧</div>
+            <div class="mini-label">{{ i18n('dashboard.deviceAlarmLabel', '鍛婅璁惧') }}</div>
             <div class="mini-value">{{ formatNumber(devices.overview.alarm) }}</div>
-            <div class="mini-hint">鍚樆濉炴垨鎶ヨ鐘舵��</div>
+            <div class="mini-hint">{{ i18n('dashboard.deviceAlarmHint', '鍚樆濉炴垨鎶ヨ鐘舵��') }}</div>
           </div>
         </div>
 
         <div class="chart-card">
-          <div class="chart-title">璁惧鍦ㄧ嚎鍒嗗竷</div>
+          <div class="chart-title">{{ i18n('dashboard.deviceTypeChartTitle', '璁惧鍦ㄧ嚎鍒嗗竷') }}</div>
           <div ref="deviceTypeChart" class="device-chart-box"></div>
         </div>
 
@@ -956,11 +956,11 @@
           <div v-for="item in devices.typeStats" :key="item.name" class="type-row">
             <div class="type-row-main">
               <div class="type-row-name">{{ item.name }}</div>
-              <div class="type-row-desc">鍦ㄧ嚎 {{ formatNumber(item.online) }} / 鎬绘暟 {{ formatNumber(item.total) }}锛岀绾� {{ formatNumber(item.offline) }}</div>
+              <div class="type-row-desc">{{ i18n('dashboard.deviceTypeDesc', '鍦ㄧ嚎 {0} / 鎬绘暟 {1}锛岀绾� {2}', [formatNumber(item.online), formatNumber(item.total), formatNumber(item.offline)]) }}</div>
             </div>
             <div class="type-row-side">
-              <el-tag size="mini" type="success">蹇欑 {{ formatNumber(item.busy) }}</el-tag>
-              <el-tag size="mini" :type="item.alarm > 0 ? 'danger' : 'info'">鍛婅 {{ formatNumber(item.alarm) }}</el-tag>
+              <el-tag size="mini" type="success">{{ i18n('dashboard.deviceBusyTag', '蹇欑 {0}', [formatNumber(item.busy)]) }}</el-tag>
+              <el-tag size="mini" :type="item.alarm > 0 ? 'danger' : 'info'">{{ i18n('dashboard.deviceAlarmTag', '鍛婅 {0}', [formatNumber(item.alarm)]) }}</el-tag>
             </div>
           </div>
         </div>
@@ -970,42 +970,42 @@
         <div class="panel-header">
           <div>
             <div class="panel-kicker">Network</div>
-            <h2 class="panel-title">璁惧缃戠粶鍒嗘瀽</h2>
-            <div class="panel-desc">姹囨�绘渶鏂� Ping 鏍锋湰鐨勮繛閫氭�с�佸欢杩熶笌寮傚父璁惧锛屽府鍔╁揩閫熷彂鐜扮綉缁滄尝鍔ㄣ��</div>
+            <h2 class="panel-title">{{ i18n('devicePingLog.title', '璁惧缃戠粶鍒嗘瀽') }}</h2>
+            <div class="panel-desc">{{ i18n('dashboard.networkPanelDesc', '姹囨�绘渶鏂� Ping 鏍锋湰鐨勮繛閫氭�с�佸欢杩熶笌寮傚父璁惧锛屽府鍔╁揩閫熷彂鐜扮綉缁滄尝鍔ㄣ��') }}</div>
           </div>
           <div class="panel-actions">
             <el-tag size="small" :type="network.overview.attentionDevices > 0 ? 'warning' : 'success'">
-              闇�鍏虫敞 {{ formatNumber(network.overview.attentionDevices) }}
+              {{ i18n('dashboard.networkAttentionTag', '闇�鍏虫敞 {0}', [formatNumber(network.overview.attentionDevices)]) }}
             </el-tag>
-            <el-button size="mini" plain @click="openDevicePingAnalysis">鏌ョ湅鏄庣粏</el-button>
+            <el-button size="mini" plain @click="openDevicePingAnalysis">{{ i18n('dashboard.networkViewDetail', '鏌ョ湅鏄庣粏') }}</el-button>
           </div>
         </div>
 
         <div class="mini-grid">
           <div class="mini-card network-mini-ok">
-            <div class="mini-label">姝e父</div>
+            <div class="mini-label">{{ i18n('dashboard.networkOkLabel', '姝e父') }}</div>
             <div class="mini-value">{{ formatNumber(network.overview.okDevices) }}</div>
-            <div class="mini-hint">鏈�鏂版牱鏈姸鎬� OK</div>
+            <div class="mini-hint">{{ i18n('dashboard.networkOkHint', '鏈�鏂版牱鏈姸鎬� OK') }}</div>
           </div>
           <div class="mini-card network-mini-warning">
-            <div class="mini-label">娉㈠姩</div>
+            <div class="mini-label">{{ i18n('dashboard.networkUnstableLabel', '娉㈠姩') }}</div>
             <div class="mini-value">{{ formatNumber(network.overview.unstableDevices) }}</div>
-            <div class="mini-hint">閮ㄥ垎鎺㈡祴鎴愬姛</div>
+            <div class="mini-hint">{{ i18n('dashboard.networkUnstableHint', '閮ㄥ垎鎺㈡祴鎴愬姛') }}</div>
           </div>
           <div class="mini-card network-mini-offline">
-            <div class="mini-label">瓒呮椂/寮傚父</div>
+            <div class="mini-label">{{ i18n('dashboard.networkOfflineLabel', '瓒呮椂/寮傚父') }}</div>
             <div class="mini-value">{{ formatNumber(network.overview.offlineDevices) }}</div>
-            <div class="mini-hint">鏆傛棤鏁版嵁 {{ formatNumber(network.overview.noDataDevices) }}</div>
+            <div class="mini-hint">{{ i18n('dashboard.networkNoDataHint', '鏆傛棤鏁版嵁 {0}', [formatNumber(network.overview.noDataDevices)]) }}</div>
           </div>
           <div class="mini-card network-mini-latency">
-            <div class="mini-label">骞冲潎寤惰繜</div>
+            <div class="mini-label">{{ i18n('dashboard.networkAvgLatencyLabel', '骞冲潎寤惰繜') }}</div>
             <div class="mini-value">{{ formatLatency(network.overview.avgLatencyMs) }}</div>
-            <div class="mini-hint">宄板�� {{ formatLatency(network.overview.maxLatencyMs) }}</div>
+            <div class="mini-hint">{{ i18n('dashboard.networkPeakLatencyHint', '宄板�� {0}', [formatLatency(network.overview.maxLatencyMs)]) }}</div>
           </div>
         </div>
 
         <div class="chart-card">
-          <div class="chart-title">杩為�氱姸鎬佸垎甯�</div>
+          <div class="chart-title">{{ i18n('dashboard.networkStatusChartTitle', '杩為�氱姸鎬佸垎甯�') }}</div>
           <div class="chart-subtitle">{{ networkSamplingText() }}</div>
           <div ref="networkStatusChart" class="network-chart-box"></div>
         </div>
@@ -1019,23 +1019,23 @@
             </div>
             <div class="route-row-side">
               <el-tag size="mini" :type="item.statusType">{{ item.statusText }}</el-tag>
-              <div class="route-extra">骞冲潎 {{ formatLatency(item.avgLatencyMs) }}</div>
-              <div class="route-extra">鏈�杩戞牱鏈� {{ displayText(item.latestTimeLabel, '-') }}</div>
+              <div class="route-extra">{{ i18n('dashboard.networkAvgLatencyTag', '骞冲潎 {0}', [formatLatency(item.avgLatencyMs)]) }}</div>
+              <div class="route-extra">{{ i18n('dashboard.networkLatestSampleTag', '鏈�杩戞牱鏈� {0}', [displayText(item.latestTimeLabel, '-')]) }}</div>
             </div>
           </div>
         </div>
         <div v-else-if="network.overview.totalDevices > 0" class="network-healthy-state">
           <div class="network-healthy-main">
-            <div class="network-healthy-title">褰撳墠缃戠粶鎺㈡祴绋冲畾</div>
-            <div class="network-healthy-desc">宸茬撼鍏� {{ formatNumber(network.overview.totalDevices) }} 鍙拌澶囷紝鏈�杩戜竴杞湭鍙戠幇瓒呮椂鎴栨尝鍔ㄣ��</div>
+            <div class="network-healthy-title">{{ i18n('dashboard.networkHealthyTitle', '褰撳墠缃戠粶鎺㈡祴绋冲畾') }}</div>
+            <div class="network-healthy-desc">{{ i18n('dashboard.networkHealthyDesc', '宸茬撼鍏� {0} 鍙拌澶囷紝鏈�杩戜竴杞湭鍙戠幇瓒呮椂鎴栨尝鍔ㄣ��', [formatNumber(network.overview.totalDevices)]) }}</div>
           </div>
           <div class="network-healthy-tags">
-            <div class="network-healthy-tag">姝e父 {{ formatNumber(network.overview.okDevices) }}</div>
-            <div class="network-healthy-tag">骞冲潎 {{ formatLatency(network.overview.avgLatencyMs) }}</div>
-            <div class="network-healthy-tag">宄板�� {{ formatLatency(network.overview.maxLatencyMs) }}</div>
+            <div class="network-healthy-tag">{{ i18n('dashboard.networkHealthyOk', '姝e父 {0}', [formatNumber(network.overview.okDevices)]) }}</div>
+            <div class="network-healthy-tag">{{ i18n('dashboard.networkHealthyAvg', '骞冲潎 {0}', [formatLatency(network.overview.avgLatencyMs)]) }}</div>
+            <div class="network-healthy-tag">{{ i18n('dashboard.networkHealthyPeak', '宄板�� {0}', [formatLatency(network.overview.maxLatencyMs)]) }}</div>
           </div>
         </div>
-        <el-empty v-else description="鏆傛棤璁惧缃戠粶鏍锋湰"></el-empty>
+        <el-empty v-else :description="i18n('dashboard.networkEmpty', '鏆傛棤璁惧缃戠粶鏍锋湰')"></el-empty>
       </section>
     </div>
   </div>
@@ -1043,8 +1043,8 @@
   <div v-if="loading" class="loading-mask">
     <div class="loading-card">
       <i class="el-icon-loading" style="font-size: 26px;"></i>
-      <div class="loading-title">姝e湪鍔犺浇浠〃鐩�</div>
-      <div class="loading-desc">姹囨�讳换鍔°�佽澶囦笌 AI 杩愯鏁版嵁锛岃绋嶅��...</div>
+      <div class="loading-title">{{ i18n('dashboard.loadingTitle', '姝e湪鍔犺浇浠〃鐩�') }}</div>
+      <div class="loading-desc">{{ i18n('dashboard.loadingDesc', '姹囨�讳换鍔°�佽澶囦笌 AI 杩愯鏁版嵁锛岃绋嶅��...') }}</div>
     </div>
   </div>
 </div>
@@ -1054,6 +1054,6 @@
 <script type="text/javascript" src="../../static/vue/js/vue.min.js"></script>
 <script type="text/javascript" src="../../static/vue/element/element.js"></script>
 <script type="text/javascript" src="../../static/js/echarts/echarts.min.js"></script>
-<script type="text/javascript" src="../../static/js/dashboard/dashboard.js?v=20260317-dashboard-network-focus"></script>
+<script type="text/javascript" src="../../static/js/dashboard/dashboard.js?v=20260317-dashboard-network-i18n"></script>
 </body>
 </html>
diff --git a/src/main/webapp/views/devicePingLog/devicePingLog.html b/src/main/webapp/views/devicePingLog/devicePingLog.html
index 6e8b7aa..09fdb60 100644
--- a/src/main/webapp/views/devicePingLog/devicePingLog.html
+++ b/src/main/webapp/views/devicePingLog/devicePingLog.html
@@ -275,106 +275,106 @@
 <body>
 <div id="app" v-cloak class="page-shell">
   <div class="page-head">
-    <div class="page-title">璁惧缃戠粶鍒嗘瀽</div>
-    <div class="page-meta">鍖呭ぇ灏� {{ formatPacketSize(samplingConfig.packetSize) }}锛寋{ samplingConfigText }}</div>
+    <div class="page-title">{{ i18n('devicePingLog.title', '璁惧缃戠粶鍒嗘瀽') }}</div>
+    <div class="page-meta">{{ i18n('devicePingLog.pageMeta', '鍖呭ぇ灏� {0}锛寋1}', [formatPacketSize(samplingConfig.packetSize), samplingConfigText]) }}</div>
   </div>
 
   <section class="summary-grid">
     <div class="summary-card">
-      <div class="summary-label">璁惧鎬绘暟</div>
-      <div class="summary-value">{{ overviewSummary.totalDevices }}</div>
-      <div class="summary-sub">宸查厤缃� IP 鐨勮澶�</div>
+      <div class="summary-label">{{ i18n('devicePingLog.summary.totalDevices', '璁惧鎬绘暟') }}</div>
+      <div class="summary-value">{{ formatNumber(overviewSummary.totalDevices) }}</div>
+      <div class="summary-sub">{{ i18n('devicePingLog.summary.totalDevicesHint', '宸查厤缃� IP 鐨勮澶�') }}</div>
     </div>
     <div class="summary-card">
-      <div class="summary-label">姝e父</div>
-      <div class="summary-value">{{ overviewSummary.okDevices }}</div>
-      <div class="summary-sub">鏈�杩戞牱鏈姸鎬� OK</div>
+      <div class="summary-label">{{ i18n('devicePingLog.summary.ok', '姝e父') }}</div>
+      <div class="summary-value">{{ formatNumber(overviewSummary.okDevices) }}</div>
+      <div class="summary-sub">{{ i18n('devicePingLog.summary.okHint', '鏈�杩戞牱鏈姸鎬� OK') }}</div>
     </div>
     <div class="summary-card">
-      <div class="summary-label">娉㈠姩</div>
-      <div class="summary-value">{{ overviewSummary.unstableDevices }}</div>
-      <div class="summary-sub">閮ㄥ垎鎺㈡祴鎴愬姛</div>
+      <div class="summary-label">{{ i18n('devicePingLog.summary.unstable', '娉㈠姩') }}</div>
+      <div class="summary-value">{{ formatNumber(overviewSummary.unstableDevices) }}</div>
+      <div class="summary-sub">{{ i18n('devicePingLog.summary.unstableHint', '閮ㄥ垎鎺㈡祴鎴愬姛') }}</div>
     </div>
     <div class="summary-card">
-      <div class="summary-label">瓒呮椂/寮傚父</div>
-      <div class="summary-value">{{ overviewSummary.offlineDevices }}</div>
-      <div class="summary-sub">鏈�杩戞牱鏈笉鍙揪</div>
+      <div class="summary-label">{{ i18n('devicePingLog.summary.offline', '瓒呮椂/寮傚父') }}</div>
+      <div class="summary-value">{{ formatNumber(overviewSummary.offlineDevices) }}</div>
+      <div class="summary-sub">{{ i18n('devicePingLog.summary.offlineHint', '鏈�杩戞牱鏈笉鍙揪') }}</div>
     </div>
     <div class="summary-card">
-      <div class="summary-label">鏆傛棤鏁版嵁</div>
-      <div class="summary-value">{{ overviewSummary.noDataDevices }}</div>
-      <div class="summary-sub">杩樻病鏈夎惤鐩樻牱鏈�</div>
+      <div class="summary-label">{{ i18n('devicePingLog.summary.noData', '鏆傛棤鏁版嵁') }}</div>
+      <div class="summary-value">{{ formatNumber(overviewSummary.noDataDevices) }}</div>
+      <div class="summary-sub">{{ i18n('devicePingLog.summary.noDataHint', '杩樻病鏈夎惤鐩樻牱鏈�') }}</div>
     </div>
     <div class="summary-card">
-      <div class="summary-label">鏁翠綋骞冲潎寤惰繜</div>
+      <div class="summary-label">{{ i18n('devicePingLog.summary.avgLatency', '鏁翠綋骞冲潎寤惰繜') }}</div>
       <div class="summary-value">{{ formatLatency(overviewSummary.avgLatencyMs) }}</div>
-      <div class="summary-sub">宄板�� {{ formatLatency(overviewSummary.maxLatencyMs) }}</div>
+      <div class="summary-sub">{{ i18n('devicePingLog.summary.peakLatency', '宄板�� {0}', [formatLatency(overviewSummary.maxLatencyMs)]) }}</div>
     </div>
   </section>
 
   <section class="panel">
     <div class="panel-head">
-      <div class="panel-title">璁惧鎬昏</div>
-      <el-button size="small" :loading="overviewLoading" @click="loadOverview">鍒锋柊</el-button>
+      <div class="panel-title">{{ i18n('devicePingLog.overviewTitle', '璁惧鎬昏') }}</div>
+      <el-button size="small" :loading="overviewLoading" @click="loadOverview">{{ i18n('devicePingLog.refresh', '鍒锋柊') }}</el-button>
     </div>
     <div class="panel-body">
       <div class="toolbar">
         <el-form :inline="true" size="small" :model="overviewFilters" style="margin-bottom: -18px;">
-          <el-form-item label="璁惧绫诲瀷">
+          <el-form-item :label="i18n('devicePingLog.filter.deviceType', '璁惧绫诲瀷')">
             <el-select v-model="overviewFilters.deviceType" clearable style="width: 120px;">
-              <el-option label="鍏ㄩ儴" value=""></el-option>
+              <el-option :label="i18n('devicePingLog.filter.all', '鍏ㄩ儴')" value=""></el-option>
               <el-option label="Crn" value="Crn"></el-option>
               <el-option label="DualCrn" value="DualCrn"></el-option>
               <el-option label="Devp" value="Devp"></el-option>
               <el-option label="Rgv" value="Rgv"></el-option>
             </el-select>
           </el-form-item>
-          <el-form-item label="鍏抽敭瀛�">
-            <el-input v-model.trim="overviewFilters.keyword" clearable style="width: 220px;" placeholder="璁惧鍙� / IP"></el-input>
+          <el-form-item :label="i18n('devicePingLog.filter.keyword', '鍏抽敭瀛�')">
+            <el-input v-model.trim="overviewFilters.keyword" clearable style="width: 220px;" :placeholder="i18n('devicePingLog.filter.keywordPlaceholder', '璁惧鍙� / IP')"></el-input>
           </el-form-item>
         </el-form>
         <div class="toolbar-right">
-          <span class="page-meta">鎬昏璁惧 {{ filteredOverviewRows.length }} 鍙�</span>
+          <span class="page-meta">{{ i18n('devicePingLog.overviewCount', '鎬昏璁惧 {0} 鍙�', [formatNumber(filteredOverviewRows.length)]) }}</span>
         </div>
       </div>
 
       <el-table :data="filteredOverviewRows" border stripe size="mini" v-loading="overviewLoading" style="width: 100%;">
-        <el-table-column label="璁惧" min-width="170">
+        <el-table-column :label="i18n('devicePingLog.column.device', '璁惧')" min-width="170">
           <template slot-scope="scope">
             {{ scope.row.deviceType }}-{{ scope.row.deviceNo }}
           </template>
         </el-table-column>
         <el-table-column prop="ip" label="IP" min-width="150"></el-table-column>
-        <el-table-column label="鐘舵��" width="110" align="center">
+        <el-table-column :label="i18n('devicePingLog.column.status', '鐘舵��')" width="110" align="center">
           <template slot-scope="scope">
             <span class="status-chip" :class="statusClass(scope.row.status)">{{ scope.row.statusText }}</span>
           </template>
         </el-table-column>
-        <el-table-column label="鎴愬姛鐜�" width="90" align="right">
+        <el-table-column :label="i18n('devicePingLog.column.successRate', '鎴愬姛鐜�')" width="90" align="right">
           <template slot-scope="scope">
             {{ formatPercent(scope.row.successRate) }}
           </template>
         </el-table-column>
-        <el-table-column label="骞冲潎" width="90" align="right">
+        <el-table-column :label="i18n('devicePingLog.column.avg', '骞冲潎')" width="90" align="right">
           <template slot-scope="scope">
             {{ formatLatency(scope.row.avgLatencyMs) }}
           </template>
         </el-table-column>
-        <el-table-column label="鏈�灏�" width="90" align="right">
+        <el-table-column :label="i18n('devicePingLog.column.min', '鏈�灏�')" width="90" align="right">
           <template slot-scope="scope">
             {{ formatLatency(scope.row.minLatencyMs) }}
           </template>
         </el-table-column>
-        <el-table-column label="鏈�澶�" width="90" align="right">
+        <el-table-column :label="i18n('devicePingLog.column.max', '鏈�澶�')" width="90" align="right">
           <template slot-scope="scope">
             {{ formatLatency(scope.row.maxLatencyMs) }}
           </template>
         </el-table-column>
-        <el-table-column prop="latestTimeLabel" label="鏇存柊鏃堕棿" width="170"></el-table-column>
-        <el-table-column prop="message" label="璇存槑" min-width="200" show-overflow-tooltip></el-table-column>
-        <el-table-column label="鎿嶄綔" width="110" align="center" fixed="right">
+        <el-table-column prop="latestTimeLabel" :label="i18n('devicePingLog.column.updateTime', '鏇存柊鏃堕棿')" width="170"></el-table-column>
+        <el-table-column prop="message" :label="i18n('devicePingLog.column.message', '璇存槑')" min-width="200" show-overflow-tooltip></el-table-column>
+        <el-table-column :label="i18n('devicePingLog.column.action', '鎿嶄綔')" width="110" align="center" fixed="right">
           <template slot-scope="scope">
-            <el-button size="mini" type="primary" plain @click="openDetail(scope.row)">鏌ョ湅璇︽儏</el-button>
+            <el-button size="mini" type="primary" plain @click="openDetail(scope.row)">{{ i18n('devicePingLog.viewDetail', '鏌ョ湅璇︽儏') }}</el-button>
           </template>
         </el-table-column>
       </el-table>
@@ -383,37 +383,37 @@
 
   <section class="panel">
     <div class="panel-head">
-      <div class="panel-title">璁惧璇︽儏</div>
+      <div class="panel-title">{{ i18n('devicePingLog.detailTitle', '璁惧璇︽儏') }}</div>
     </div>
     <div class="panel-body">
-      <div v-if="!currentDevice" class="empty-shell">浠庝笂鏂硅澶囨�昏閫夋嫨涓�鍙拌澶囨煡鐪嬬绾ф槑缁�</div>
+      <div v-if="!currentDevice" class="empty-shell">{{ i18n('devicePingLog.detailEmpty', '浠庝笂鏂硅澶囨�昏閫夋嫨涓�鍙拌澶囨煡鐪嬬绾ф槑缁�') }}</div>
       <div v-else class="detail-shell">
         <div class="detail-toolbar">
           <div class="detail-selected">{{ currentDevice.label }}</div>
           <div class="toolbar-right">
             <el-form :inline="true" size="small" :model="detailFilters" style="margin-bottom: -18px;">
-              <el-form-item label="鏃堕棿鑼冨洿">
+              <el-form-item :label="i18n('devicePingLog.filter.timeRange', '鏃堕棿鑼冨洿')">
                 <el-date-picker
                   v-model="detailFilters.range"
                   type="datetimerange"
                   unlink-panels
-                  range-separator="鑷�"
-                  start-placeholder="寮�濮�"
-                  end-placeholder="缁撴潫"
+                  :range-separator="i18n('devicePingLog.filter.rangeSeparator', '鑷�')"
+                  :start-placeholder="i18n('devicePingLog.filter.start', '寮�濮�')"
+                  :end-placeholder="i18n('devicePingLog.filter.end', '缁撴潫')"
                   style="width: 360px;">
                 </el-date-picker>
               </el-form-item>
               <el-form-item>
-                <el-button @click="setQuickRange(30)">30 鍒嗛挓</el-button>
+                <el-button @click="setQuickRange(30)">{{ i18n('devicePingLog.quickRange.30m', '30 鍒嗛挓') }}</el-button>
               </el-form-item>
               <el-form-item>
-                <el-button @click="setQuickRange(60)">1 灏忔椂</el-button>
+                <el-button @click="setQuickRange(60)">{{ i18n('devicePingLog.quickRange.1h', '1 灏忔椂') }}</el-button>
               </el-form-item>
               <el-form-item>
-                <el-button @click="setQuickRange(360)">6 灏忔椂</el-button>
+                <el-button @click="setQuickRange(360)">{{ i18n('devicePingLog.quickRange.6h', '6 灏忔椂') }}</el-button>
               </el-form-item>
               <el-form-item>
-                <el-button type="primary" :loading="detailLoading" @click="queryTrend">鏌ヨ</el-button>
+                <el-button type="primary" :loading="detailLoading" @click="queryTrend">{{ i18n('devicePingLog.query', '鏌ヨ') }}</el-button>
               </el-form-item>
             </el-form>
           </div>
@@ -421,49 +421,49 @@
 
         <div class="detail-summary-grid">
           <div class="detail-card">
-            <div class="detail-card-label">鐘舵��</div>
+            <div class="detail-card-label">{{ i18n('devicePingLog.detail.status', '鐘舵��') }}</div>
             <div class="detail-card-value">{{ detailSummary.latestStatus || '--' }}</div>
           </div>
           <div class="detail-card">
-            <div class="detail-card-label">鍖呭ぇ灏�</div>
+            <div class="detail-card-label">{{ i18n('devicePingLog.detail.packetSize', '鍖呭ぇ灏�') }}</div>
             <div class="detail-card-value">{{ formatPacketSize(detailSummary.packetSize) }}</div>
           </div>
           <div class="detail-card">
-            <div class="detail-card-label">鎴愬姛鐜�</div>
+            <div class="detail-card-label">{{ i18n('devicePingLog.detail.successRate', '鎴愬姛鐜�') }}</div>
             <div class="detail-card-value">{{ formatPercent(detailSummary.successRate) }}</div>
           </div>
           <div class="detail-card">
-            <div class="detail-card-label">骞冲潎</div>
+            <div class="detail-card-label">{{ i18n('devicePingLog.detail.avg', '骞冲潎') }}</div>
             <div class="detail-card-value">{{ formatLatency(detailSummary.avgLatencyMs) }}</div>
           </div>
           <div class="detail-card">
-            <div class="detail-card-label">鏈�灏�</div>
+            <div class="detail-card-label">{{ i18n('devicePingLog.detail.min', '鏈�灏�') }}</div>
             <div class="detail-card-value">{{ formatLatency(detailSummary.minLatencyMs) }}</div>
           </div>
           <div class="detail-card">
-            <div class="detail-card-label">鏈�澶�</div>
+            <div class="detail-card-label">{{ i18n('devicePingLog.detail.max', '鏈�澶�') }}</div>
             <div class="detail-card-value">{{ formatLatency(detailSummary.maxLatencyMs) }}</div>
           </div>
         </div>
 
         <div class="chart-grid">
           <div class="chart-card">
-            <div class="chart-title">寤惰繜</div>
-            <div v-if="!series.length && !detailLoading" class="empty-shell">褰撳墠鑼冨洿鏆傛棤绉掔骇鏍锋湰</div>
+            <div class="chart-title">{{ i18n('devicePingLog.chart.latencyTitle', '寤惰繜') }}</div>
+            <div v-if="!series.length && !detailLoading" class="empty-shell">{{ i18n('devicePingLog.chart.empty', '褰撳墠鑼冨洿鏆傛棤绉掔骇鏍锋湰') }}</div>
             <div v-show="series.length || detailLoading" ref="latencyChart" class="chart-box"></div>
           </div>
           <div class="chart-card">
-            <div class="chart-title">鎴愬姛鐜� / 澶辫触娆℃暟</div>
-            <div v-if="!series.length && !detailLoading" class="empty-shell">褰撳墠鑼冨洿鏆傛棤绉掔骇鏍锋湰</div>
+            <div class="chart-title">{{ i18n('devicePingLog.chart.availabilityTitle', '鎴愬姛鐜� / 澶辫触娆℃暟') }}</div>
+            <div v-if="!series.length && !detailLoading" class="empty-shell">{{ i18n('devicePingLog.chart.empty', '褰撳墠鑼冨洿鏆傛棤绉掔骇鏍锋湰') }}</div>
             <div v-show="series.length || detailLoading" ref="availabilityChart" class="chart-box"></div>
           </div>
         </div>
 
         <el-table :data="alerts" border stripe size="mini" style="width: 100%;">
-          <el-table-column prop="timeLabel" label="鏃堕棿" width="180"></el-table-column>
-          <el-table-column prop="status" label="鐘舵��" width="110"></el-table-column>
+          <el-table-column prop="timeLabel" :label="i18n('devicePingLog.alertColumn.time', '鏃堕棿')" width="180"></el-table-column>
+          <el-table-column prop="status" :label="i18n('devicePingLog.alertColumn.status', '鐘舵��')" width="110"></el-table-column>
           <el-table-column prop="ip" label="IP" width="150"></el-table-column>
-          <el-table-column prop="message" label="璇存槑" min-width="240" show-overflow-tooltip></el-table-column>
+          <el-table-column prop="message" :label="i18n('devicePingLog.alertColumn.message', '璇存槑')" min-width="240" show-overflow-tooltip></el-table-column>
         </el-table>
       </div>
     </div>
@@ -475,6 +475,6 @@
 <script type="text/javascript" src="../../static/vue/js/vue.min.js"></script>
 <script type="text/javascript" src="../../static/vue/element/element.js"></script>
 <script type="text/javascript" src="../../static/js/echarts/echarts.min.js"></script>
-<script type="text/javascript" src="../../static/js/devicePingLog/devicePingLog.js?v=20260316_device_ping_packet_size_detail" charset="utf-8"></script>
+<script type="text/javascript" src="../../static/js/devicePingLog/devicePingLog.js?v=20260317_device_ping_i18n" charset="utf-8"></script>
 </body>
 </html>

--
Gitblit v1.9.1