From d6e4a6a0893df1c5b28230984527fae7d92d364d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=B6=85=E5=93=A5?= <2982212683@qq.com>
Date: Sun, 24 May 2026 21:29:34 +0800
Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=AF=AD=E8=A8=80=E8=A1=A5?=
=?UTF-8?q?=E4=B8=81=E7=9A=84=E5=91=BD=E5=90=8D=E7=A9=BA=E9=97=B4,?=
=?UTF-8?q?=E4=BC=98=E5=8C=96=E5=8E=86=E5=8F=B2=E6=97=A5=E5=BF=97=E7=9A=84?=
=?UTF-8?q?=E6=9F=A5=E8=AF=A2=E6=80=A7=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
apps/common/monitor/routers.py | 184 +-
docs/spec/MDS-i18n-Implementation-Proposal.md | 1112 ++++++++++
docs/spec/MDS-i18n-Task-Plan.md | 1926 +++++++++++++++++
static/lib/i18n/de-DE.js | 845 ++++----
static/lib/i18n/en-US.js | 845 ++++----
static/lib/i18n/zh-CN.js | 845 ++++----
static/monitor/history-logs.html | 529 +++--
static/monitor/index.html | 314 +--
static/monitor/js/monitor.js | 358 +--
static/monitor/live-logs.html | 36 +-
10 files changed, 5183 insertions(+), 1811 deletions(-)
create mode 100644 docs/spec/MDS-i18n-Implementation-Proposal.md
create mode 100644 docs/spec/MDS-i18n-Task-Plan.md
diff --git a/apps/common/monitor/routers.py b/apps/common/monitor/routers.py
index 6f6e383..d22aeeb 100644
--- a/apps/common/monitor/routers.py
+++ b/apps/common/monitor/routers.py
@@ -4,6 +4,20 @@
提供监控相关的 API 端点
"""
+import time
+import json
+import asyncio
+from datetime import datetime, timedelta, timezone
+from fastapi import APIRouter, HTTPException, WebSocket
+from fastapi.responses import JSONResponse, StreamingResponse
+from typing import Dict, Any, List, Optional
+from tortoise import Tortoise
+from .service import monitor_service
+from .log_stream_service import log_stream_service
+from .storage import request_storage, outbound_request_storage, system_log_storage
+from .models import APIRequest, OutboundAPIRequest, SystemLog
+from core.settings import TIMEZONE
+from globalobjects import logger as log_config
import time
import json
import asyncio
@@ -1030,6 +1044,8 @@ async def get_history_by_time_range(
http_requests = await request_storage.get_requests_with_filters(
utc_start, utc_end, limit, filter_params
)
+ print(f"[DEBUG] HTTP查询: page={page}, page_size={page_size}, 返回{len(http_requests)}条")
+ # 列表查询只返回基本字段,不返回request_body/response_body大字段(详情时懒加载)
result["http_requests"] = [{
"id": req.id,
"timestamp": req.timestamp.isoformat(),
@@ -1042,11 +1058,8 @@ async def get_history_by_time_range(
"user_agent": req.user_agent,
"is_slow": req.is_slow,
"is_error": req.is_error,
- "error_message": req.error_message,
- "request_body": req.request_body,
- "response_body": req.response_body,
- "request_headers": getattr(req, 'request_headers', None),
- "response_headers": getattr(req, 'response_headers', None)
+ "error_message": req.error_message
+ # 不返回: request_body, response_body, request_headers, response_headers
} for req in http_requests]
# 查询发送请求
@@ -1054,6 +1067,8 @@ async def get_history_by_time_range(
outbound_requests = await outbound_request_storage.get_requests_with_filters(
utc_start, utc_end, limit, filter_params
)
+ print(f"[DEBUG] Outbound查询: page={page}, page_size={page_size}, 返回{len(outbound_requests)}条")
+ # 列表查询只返回基本字段,不返回request_body/response_body大字段
result["outbound_requests"] = [{
"id": req.id,
"timestamp": req.timestamp.isoformat(),
@@ -1064,11 +1079,8 @@ async def get_history_by_time_range(
"module": req.module,
"is_slow": req.is_slow,
"is_error": req.is_error,
- "error_message": req.error_message,
- "request_body": req.request_body,
- "response_body": req.response_body,
- "request_headers": req.request_headers,
- "response_headers": req.response_headers
+ "error_message": req.error_message
+ # 不返回: request_body, response_body, request_headers, response_headers
} for req in outbound_requests]
# 查询系统日志
@@ -1076,6 +1088,8 @@ async def get_history_by_time_range(
logs = await system_log_storage.get_logs_with_filters(
utc_start, utc_end, level, limit, filter_params
)
+ print(f"[DEBUG] Logs查询: page={page}, page_size={page_size}, 返回{len(logs)}条")
+ # 列表查询不返回stack_trace大字段(详情时懒加载)
result["logs"] = [{
"id": log.id,
"timestamp": log.timestamp.isoformat(),
@@ -1083,18 +1097,25 @@ async def get_history_by_time_range(
"module": log.module,
"function": log.function,
"line_number": log.line_number,
- "message": log.message,
- "stack_trace": log.stack_trace
+ "message": log.message[:500] if log.message else None # 截断message,避免超长日志
+ # 不返回: stack_trace
} for log in logs]
- # 计算分页元数据
+ # 统计总数(用于结果摘要显示)- 只执行一次count
+ result["stats"] = {
+ "http_count": await request_storage.count_requests_with_filters(utc_start, utc_end, filter_params) if should_query_http else 0,
+ "outbound_count": await outbound_request_storage.count_requests_with_filters(utc_start, utc_end, filter_params) if should_query_outbound else 0,
+ "logs_count": await system_log_storage.count_logs_with_filters(utc_start, utc_end, level, filter_params) if should_query_logs else 0
+ }
+
+ # 计算分页元数据(复用stats的count结果)
total_count = 0
if should_query_http:
- total_count = await request_storage.count_requests_with_filters(utc_start, utc_end, filter_params)
+ total_count = result["stats"]["http_count"]
elif should_query_outbound:
- total_count = await outbound_request_storage.count_requests_with_filters(utc_start, utc_end, filter_params)
+ total_count = result["stats"]["outbound_count"]
elif should_query_logs:
- total_count = await system_log_storage.count_logs_with_filters(utc_start, utc_end, level, filter_params)
+ total_count = result["stats"]["logs_count"]
if total_count > 0:
total_pages = (total_count + page_size - 1) // page_size
@@ -1109,21 +1130,36 @@ async def get_history_by_time_range(
"end_index": min(page * page_size, total_count)
}
- # 统计总数(用于结果摘要显示)
- result["stats"] = {
- "http_count": await request_storage.count_requests_with_filters(utc_start, utc_end, filter_params) if should_query_http else 0,
- "outbound_count": await outbound_request_storage.count_requests_with_filters(utc_start, utc_end, filter_params) if should_query_outbound else 0,
- "logs_count": await system_log_storage.count_logs_with_filters(utc_start, utc_end, level, filter_params) if should_query_logs else 0
- }
-
- # 统计日志级别分布(仅查询系统日志时)
+ # 统计日志级别分布(仅查询系统日志时)- 优化:使用GROUP BY聚合,避免查询所有记录
if should_query_logs and result["stats"]["logs_count"] > 0:
- level_stats = {}
- for lv in ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']:
- count = await system_log_storage.count_logs_with_filters(utc_start, utc_end, lv, filter_params)
- if count > 0:
- level_stats[lv] = count
- result["stats"]["level_distribution"] = level_stats
+ try:
+ # 使用原生SQL的GROUP BY聚合,性能最优
+ from .models import SystemLog
+ from tortoise import connections
+
+ conn = connections.get("default")
+
+ # 构建SQL查询
+ sql = "SELECT level, COUNT(*) as count FROM system_logs WHERE 1=1"
+ params = []
+
+ if utc_start and utc_end:
+ sql += " AND timestamp >= $1 AND timestamp <= $2"
+ params.extend([utc_start, utc_end])
+
+ if filter_params and filter_params.get('module'):
+ sql += f" AND module IN ({','.join(['$'+str(i+3) for i in range(len(filter_params['module']))])})"
+ params.extend(filter_params['module'])
+
+ sql += " GROUP BY level"
+
+ # 执行查询
+ rows = await conn.execute_query(sql, params)
+ level_stats = {row['level']: row['count'] for row in rows[1] if row.get('level')}
+ result["stats"]["level_distribution"] = level_stats
+ except Exception as e:
+ print(f"统计级别分布失败: {e}")
+ result["stats"]["level_distribution"] = {}
return result
@@ -1576,3 +1612,91 @@ async def export_history_data(
media_type="application/json",
headers={"Content-Disposition": f"attachment; filename={filename}"}
)
+
+
+# ========== 详情懒加载端点 ==========
+
+@router.get("/history/outbound/{request_id}")
+async def get_outbound_request_detail(request_id: int):
+ """
+ 获取发送请求详情(懒加载,包含request_body、response_body等大字段)
+ """
+ try:
+ req = await OutboundAPIRequest.get_or_none(id=request_id)
+ if not req:
+ raise HTTPException(status_code=404, detail="请求不存在")
+
+ return {
+ "id": req.id,
+ "timestamp": req.timestamp.isoformat(),
+ "method": req.method,
+ "url": req.url,
+ "status_code": req.status_code,
+ "duration": req.duration * 1000,
+ "module": req.module,
+ "is_slow": req.is_slow,
+ "is_error": req.is_error,
+ "error_message": req.error_message,
+ "request_body": req.request_body,
+ "response_body": req.response_body,
+ "request_headers": req.request_headers,
+ "response_headers": req.response_headers
+ }
+ except Exception as e:
+ raise HTTPException(status_code=500, detail=f"查询详情失败: {str(e)}")
+
+
+@router.get("/history/http/{request_id}")
+async def get_http_request_detail(request_id: int):
+ """
+ 获取HTTP请求详情(懒加载,包含request_body、response_body等大字段)
+ """
+ try:
+ req = await APIRequest.get_or_none(id=request_id)
+ if not req:
+ raise HTTPException(status_code=404, detail="请求不存在")
+
+ return {
+ "id": req.id,
+ "timestamp": req.timestamp.isoformat(),
+ "method": req.method,
+ "path": req.path,
+ "query_params": req.query_params,
+ "status_code": req.status_code,
+ "duration": req.response_time,
+ "client_ip": req.client_ip,
+ "user_agent": req.user_agent,
+ "is_slow": req.is_slow,
+ "is_error": req.is_error,
+ "error_message": req.error_message,
+ "request_body": req.request_body,
+ "response_body": req.response_body,
+ "request_headers": getattr(req, 'request_headers', None),
+ "response_headers": getattr(req, 'response_headers', None)
+ }
+ except Exception as e:
+ raise HTTPException(status_code=500, detail=f"查询详情失败: {str(e)}")
+
+
+@router.get("/history/log/{log_id}")
+async def get_log_detail(log_id: int):
+ """
+ 获取系统日志详情(懒加载,包含stack_trace等大字段)
+ """
+ try:
+ log = await SystemLog.get_or_none(id=log_id)
+ if not log:
+ raise HTTPException(status_code=404, detail="日志不存在")
+
+ return {
+ "id": log.id,
+ "timestamp": log.timestamp.isoformat(),
+ "level": log.level,
+ "module": log.module,
+ "function": log.function,
+ "line_number": log.line_number,
+ "message": log.message,
+ "stack_trace": log.stack_trace
+ }
+ except Exception as e:
+ raise HTTPException(status_code=500, detail=f"查询详情失败: {str(e)}")
diff --git a/docs/spec/MDS-i18n-Implementation-Proposal.md b/docs/spec/MDS-i18n-Implementation-Proposal.md
new file mode 100644
index 0000000..527c6a1
--- /dev/null
+++ b/docs/spec/MDS-i18n-Implementation-Proposal.md
@@ -0,0 +1,1112 @@
+# MDS模块国际化实施方案
+
+**文档类型**:技术方案
+**创建日期**:2026-05-24
+**版本**:v1.0
+**状态**:待评审
+
+---
+
+## 一、项目概况
+
+### 1.1 背景
+
+监控模块(Monitor)已完成国际化支持,支持中文、英文、德语三语切换。现需对MDS(数据清洗管理)模块实施相同的国际化改造。
+
+### 1.2 目标
+
+- 支持简体中文、英语、德语三种语言(与Monitor模块保持一致)
+- 与Monitor模块使用统一的国际化框架
+- 用户切换语言时,Monitor和MDS同时生效
+- 保持代码简洁,避免重复实现
+- 支持无刷新语言切换,保持页面状态
+
+### 1.3 范围
+
+- 3个HTML页面
+- 7个JavaScript文件
+- 约5,682行代码
+- 约746处中文文本需翻译
+
+---
+
+## 二、当前状态分析
+
+### 2.1 文件结构
+
+**HTML页面**(3个):
+
+| 文件 | 行数 | 用途 |
+|------|------|------|
+| `index.html` | 318 | 首页:模块卡片导航、全局操作 |
+| `user-guide.html` | 202 | 操作指引页面 |
+| `pages/template.html` | 258 | 数据操作页面模板 |
+
+**JavaScript文件**(7个):
+
+| 文件 | 行数 | 功能 |
+|------|------|------|
+| `mds-page-controller.js` | 1892 | 页面控制器(核心) |
+| `data-table.js` | 1517 | 数据表格组件 |
+| `modal-manager.js` | 564 | 模态框管理器 |
+| `common.js` | 540 | 公共函数库、状态枚举 |
+| `form-renderer.js` | 346 | 表单渲染器 |
+| `global-ops-controller.js` | 343 | 全局操作控制器 |
+| `status-card.js` | 302 | 状态统计卡片 |
+
+**总代码量**:5,682行
+
+### 2.2 文本分类统计
+
+| 优先级 | 类型 | 数量 | 示例 |
+|--------|------|------|------|
+| P0 | 状态标签 | 28处 | 待处理、已推送 |
+| P1 | 操作按钮 | 45处 | 导入、校验、推送 |
+| P2 | 导航菜单 | 14处 | 物料、BOM、模具 |
+| P3 | 弹窗文本 | 120处 | 确认、取消、提示 |
+| P4 | 错误消息 | 85处 | 查询失败、上传失败 |
+| P5 | 表格文本 | 150处 | 编辑、删除、分页 |
+| P6 | 其他提示 | 304处 | 加载中、暂无数据 |
+| **合计** | | **746处** | |
+
+### 2.3 复杂度评估
+
+**等级**:⭐⭐⭐⭐☆(高复杂度)
+
+**高复杂度因素**:
+- 硬编码文本数量庞大(746处)
+- 动态拼接文本多(需参数化翻译)
+- 组件嵌套深(表格、表单、弹窗多层嵌套)
+
+**低复杂度因素**:
+- 代码结构清晰,模块化设计
+- 页面数量少,模板复用率高
+- 无第三方UI组件库
+
+---
+
+## 三、语言包组织方案
+
+### 3.1 方案对比
+
+| 方案 | 目录结构 | 支持语言 | 优点 | 缺点 | 推荐度 |
+|------|----------|----------|------|------|--------|
+| **A. 统一管理** | `static/lib/i18n/` | 中/英/德 | 框架复用
全局切换
维护简单
与Monitor一致 | 单文件较大 | ⭐⭐⭐⭐⭐ |
+| **B. 独立目录** | `static/mds/locales/` | 中/英/德 | 模块独立
按需加载 | 重复代码
切换复杂 | ⭐⭐☆☆☆ |
+| **C. 命名空间** | `static/lib/i18n/{module}/` | 中/英/德 | 框架复用
模块隔离 | 需改造框架 | ⭐⭐⭐☆☆ |
+
+### 3.2 推荐方案:A(统一管理 + 命名空间)
+
+**目录结构**:
+```
+static/lib/i18n/
+├── i18n.js # 统一框架(已存在)
+├── zh-CN.js # 中文语言包(扩展)
+├── en-US.js # 英文语言包(扩展)
+└── de-DE.js # 德语语言包(扩展)
+```
+
+**语言包结构**(命名空间):
+```javascript
+// zh-CN.js(简体中文)
+window.__i18n_zh_CN__ = {
+ // ========== 通用文本 ==========
+ 'common.confirm': '确定',
+ 'common.cancel': '取消',
+ 'common.loading': '加载中...',
+
+ // ========== Monitor模块 ==========
+ 'monitor.page.title': 'MyAPI 系统监控面板',
+ 'monitor.status.healthy': '● 系统正常',
+ // ... (现有405条)
+
+ // ========== MDS模块 ==========
+ 'mds.app.title': '数据清洗管理系统',
+ 'mds.nav.material': '物料',
+ 'mds.action.validate': '校验',
+ 'mds.status.pending': '待处理',
+ // ... (新增约750条)
+};
+
+// en-US.js(英语)
+window.__i18n_en_US__ = {
+ // ========== Common ==========
+ 'common.confirm': 'Confirm',
+ 'common.cancel': 'Cancel',
+ 'common.loading': 'Loading...',
+
+ // ========== Monitor Module ==========
+ 'monitor.page.title': 'MyAPI System Monitor',
+ 'monitor.status.healthy': '● System Healthy',
+ // ... (现有405条)
+
+ // ========== MDS Module ==========
+ 'mds.app.title': 'Data Staging Management System',
+ 'mds.nav.material': 'Material',
+ 'mds.action.validate': 'Validate',
+ 'mds.status.pending': 'Pending',
+ // ... (新增约750条)
+};
+
+// de-DE.js(德语)
+window.__i18n_de_DE__ = {
+ // ========== Allgemein ==========
+ 'common.confirm': 'Bestätigen',
+ 'common.cancel': 'Abbrechen',
+ 'common.loading': 'Laden...',
+
+ // ========== Monitor-Modul ==========
+ 'monitor.page.title': 'MyAPI Systemüberwachung',
+ 'monitor.status.healthy': '● System gesund',
+ // ... (现有405条)
+
+ // ========== MDS-Modul ==========
+ 'mds.app.title': 'Daten-Staging-Verwaltungssystem',
+ 'mds.nav.material': 'Material',
+ 'mds.action.validate': 'Validieren',
+ 'mds.status.pending': 'Ausstehend',
+ // ... (新增约750条)
+};
+```
+
+### 3.3 方案优势
+
+1. ✅ **框架复用**:Monitor的i18n.js已成熟,无需重复开发
+2. ✅ **全局切换**:用户切换语言,Monitor和MDS同时生效
+3. ✅ **代码简洁**:避免多套i18n实例,降低复杂度
+4. ✅ **维护方便**:语言包集中管理,便于批量修改
+5. ✅ **命名清晰**:通过`monitor.`和`mds.`前缀区分模块
+
+### 3.4 语言包大小预估
+
+| 模块 | 翻译条目 |
+|------|----------|
+| Monitor(现有) | 405条 |
+| MDS(新增) | 750条 |
+| **合并后** | **约1155条/语言** |
+| **文件大小** | **约60-80KB** |
+
+单文件大小可接受,无需拆分。
+
+---
+
+## 四、实施计划
+
+### 4.1 分阶段实施(推荐)
+
+**Phase 1**:基础准备 + P0/P1(状态+按钮)
+- 工作量:4天
+- 输出:框架复用、三语言包创建、状态枚举改造
+
+**Phase 2**:P2/P3(导航+弹窗)
+- 工作量:5天
+- 输出:HTML改造、弹窗翻译
+
+**Phase 3**:P4/P5(错误+表格)
+- 工作量:6天
+- 输出:错误处理、表格组件改造
+
+**Phase 4**:P6(其他提示)
+- 工作量:4天
+- 输出:完成所有翻译、全面测试
+
+**总工作量**:19人天(约4周)
+
+**说明**:相比Monitor模块的4人日,MDS模块工作量更大,原因:
+1. 翻译文本数量更多(746处 vs 158处)
+2. 三语言翻译工作量增加50%
+3. 状态枚举、表格组件改造复杂度高
+4. 动态消息参数化处理更复杂
+
+### 4.2 详细实施步骤
+
+#### Phase 1: 基础准备(4天)
+
+**Day 1: 框架复用与语言包创建**
+- [ ] 在现有语言包中添加MDS命名空间
+- [ ] 提取P0级文本(状态标签,28处)
+- [ ] 提取P1级文本(操作按钮,45处)
+- [ ] 编写简体中文、英语、德语三语言翻译
+
+**Day 2: 状态枚举改造**
+- [ ] 改造`common.js`的`STAGING_STATUS`
+- [ ] 将label改为getter动态翻译
+- [ ] 改造`status-card.js`
+- [ ] 测试三语言状态标签翻译
+
+**Day 3-4: 验证与测试**
+- [ ] 测试状态标签三语言翻译
+- [ ] 测试操作按钮三语言翻译
+- [ ] 测试语言切换功能(无刷新)
+- [ ] 测试德语文本布局适配
+
+#### Phase 2: 导航与弹窗(5天)
+
+**Day 5-6: HTML改造**
+- [ ] `index.html`添加data-i18n属性
+- [ ] `template.html`添加data-i18n属性
+- [ ] `user-guide.html`添加data-i18n属性
+- [ ] 提取P2级文本(导航菜单,14处)
+- [ ] 测试三语言HTML文本翻译
+
+**Day 7-9: 弹窗改造**
+- [ ] 改造`modal-manager.js`
+- [ ] 改造`form-renderer.js`
+- [ ] 提取P3级文本(弹窗文本,120处)
+- [ ] 编写三语言翻译
+- [ ] 测试弹窗三语言显示
+
+#### Phase 3: 错误与表格(6天)
+
+**Day 10-11: 错误处理**
+- [ ] 改造错误消息处理函数
+- [ ] 创建后端错误码+参数处理机制
+- [ ] 提取P4级文本(错误消息,85处)
+- [ ] 编写三语言翻译
+- [ ] 测试三语言错误提示
+
+**Day 12-15: 表格改造**
+- [ ] 改造`data-table.js`
+- [ ] 改造分页控件
+- [ ] 改造空状态提示
+- [ ] 提取P5级文本(表格文本,150处)
+- [ ] 编写三语言翻译
+- [ ] 测试表格三语言显示
+
+#### Phase 4: 完成与测试(4天)
+
+**Day 16-17: 完成翻译**
+- [ ] 提取P6级文本(其他提示,304处)
+- [ ] 改造`mds-page-controller.js`
+- [ ] 改造`global-ops-controller.js`
+- [ ] 编写三语言翻译
+
+**Day 18-19: 测试验证**
+- [ ] 功能测试:所有页面文本翻译
+- [ ] 三语言切换测试(无刷新)
+- [ ] 德语文本布局验证
+- [ ] 回归测试:现有功能不受影响
+- [ ] 编写实施文档
+
+---
+
+## 五、技术方案
+
+### 5.1 状态枚举改造
+
+**改造前**:
+```javascript
+const STAGING_STATUS = {
+ PENDING: {
+ value: 'pending',
+ label: '待处理',
+ color: '#ff9300',
+ icon: '⏳'
+ }
+};
+```
+
+**改造后**:
+```javascript
+const STAGING_STATUS = {
+ PENDING: {
+ value: 'pending',
+ labelKey: 'mds.status.pending', // 翻译键
+ color: '#ff9300',
+ icon: '⏳',
+ get label() {
+ return typeof i18n !== 'undefined' ?
+ i18n.t(this.labelKey) : '待处理';
+ }
+ }
+};
+```
+
+### 5.2 动态消息参数化
+
+**改造前**:
+```javascript
+showMessage(`校验完成: 通过${pass}条,失败${fail}条`, 'success');
+```
+
+**改造后**:
+```javascript
+const msg = typeof i18n !== 'undefined' ?
+ i18n.t('mds.validation.complete', { pass, fail }) :
+ `校验完成: 通过${pass}条,失败${fail}条`;
+showMessage(msg, 'success');
+```
+
+**语言包定义**:
+```javascript
+// zh-CN.js(简体中文)
+'mds.validation.complete': '校验完成: 通过{pass}条,失败{fail}条',
+
+// en-US.js(英语)
+'mds.validation.complete': 'Validation: {pass} passed, {fail} failed',
+
+// de-DE.js(德语)
+'mds.validation.complete': 'Validierung: {pass} bestanden, {fail} fehlgeschlagen',
+```
+
+### 5.3 后端错误消息处理
+
+**方案:后端返回错误码+参数,前端统一翻译**
+
+**后端响应格式**:
+```python
+# 后端返回错误码和参数
+{
+ "success": false,
+ "error": {
+ "code": "DUPLICATE_KEY", # 错误码
+ "params": { # 动态参数
+ "field": "MaterialNo",
+ "value": "MAT001"
+ }
+ }
+}
+```
+
+**前端翻译处理**:
+```javascript
+// 错误码翻译映射表(在语言包中定义)
+// zh-CN.js
+'mds.error.DUPLICATE_KEY': '数据重复: {field}={value} 已存在',
+'mds.error.INVALID_FORMAT': '格式错误: {field} 格式不正确',
+'mds.error.FOREIGN_KEY_VIOLATION': '外键约束违反: {field} 引用不存在',
+'mds.error.VALIDATION_FAILED': '校验失败: {reason}',
+
+// en-US.js
+'mds.error.DUPLICATE_KEY': 'Duplicate data: {field}={value} already exists',
+'mds.error.INVALID_FORMAT': 'Invalid format: {field} format is incorrect',
+'mds.error.FOREIGN_KEY_VIOLATION': 'Foreign key violation: {field} reference not found',
+'mds.error.VALIDATION_FAILED': 'Validation failed: {reason}',
+
+// de-DE.js
+'mds.error.DUPLICATE_KEY': 'Doppelter Dateneintrag: {field}={value} bereits vorhanden',
+'mds.error.INVALID_FORMAT': 'Ungültiges Format: {field} Format ist fehlerhaft',
+'mds.error.FOREIGN_KEY_VIOLATION': 'Fremdschlüssel-Verletzung: {field} Referenz nicht gefunden',
+'mds.error.VALIDATION_FAILED': 'Validierung fehlgeschlagen: {reason}',
+
+// 翻译函数
+function translateError(response) {
+ if (!response.error) {
+ return response.message || 'Unknown error';
+ }
+
+ const { code, params = {} } = response.error;
+ const errorKey = `mds.error.${code}`;
+
+ // 使用i18n翻译
+ if (typeof i18n !== 'undefined') {
+ return i18n.t(errorKey, params);
+ }
+
+ // 回退:返回错误码
+ return code;
+}
+
+// 使用示例
+async function handleApiResponse(response) {
+ if (!response.success) {
+ const errorMsg = translateError(response);
+ showError(errorMsg);
+ }
+}
+```
+
+**方案优势**:
+- ✅ 后端无需关心具体翻译文本,只需返回错误码
+- ✅ 前端统一翻译,支持多语言
+- ✅ 错误码和参数分离,灵活性高
+- ✅ 新增错误类型只需在语言包中添加翻译项
+
+### 5.4 HTML模板改造
+
+**改造前**:
+```html
+
+```
+
+**改造后**:
+```html
+
+```
+
+**初始化翻译**:
+```javascript
+// 页面加载后
+document.querySelectorAll('[data-i18n]').forEach(el => {
+ const key = el.getAttribute('data-i18n');
+ const text = typeof i18n !== 'undefined' ? i18n.t(key) : key;
+ el.textContent = text;
+});
+```
+
+### 5.5 语言切换UI集成
+
+**在导航栏添加语言选择器**:
+```html
+
+```
+
+**无刷新语言切换方案**:
+```javascript
+/**
+ * 语言切换(无刷新)
+ * 保持页面状态,动态更新所有文本
+ */
+async function switchLanguage(lang) {
+ if (typeof i18n === 'undefined') {
+ console.warn('i18n not initialized');
+ return;
+ }
+
+ // 调用i18n框架切换语言
+ await i18n.switchLanguage(lang);
+
+ // 更新状态枚举(使用getter动态翻译,已自动更新)
+ // 无需手动处理
+
+ // 更新动态生成的DOM元素
+ updateDynamicElements();
+
+ // 触发自定义事件(供组件监听)
+ window.dispatchEvent(new CustomEvent('languageChanged', {
+ detail: { lang: lang }
+ }));
+
+ console.log(`[i18n] Language switched to ${lang} (no page reload)`);
+}
+
+/**
+ * 更新动态生成的DOM元素
+ * 如:表格内容、弹窗内容、状态卡片等
+ */
+function updateDynamicElements() {
+ // 1. 更新表格内容(如果表格已渲染)
+ if (window.dataTableInstance) {
+ window.dataTableInstance.refresh();
+ }
+
+ // 2. 更新状态卡片
+ if (window.statusCardInstance) {
+ window.statusCardInstance.refresh();
+ }
+
+ // 3. 更新已打开的模态框
+ const modals = document.querySelectorAll('.modal.show');
+ modals.forEach(modal => {
+ // 重新应用翻译到模态框内的元素
+ modal.querySelectorAll('[data-i18n]').forEach(el => {
+ const key = el.getAttribute('data-i18n');
+ el.textContent = i18n.t(key);
+ });
+ });
+
+ // 4. 更新表格操作按钮(编辑、删除等)
+ document.querySelectorAll('[data-i18n-action]').forEach(el => {
+ const key = el.getAttribute('data-i18n-action');
+ el.textContent = i18n.t(key);
+ });
+}
+
+// 监听语言切换事件(组件可订阅)
+window.addEventListener('languageChanged', (e) => {
+ const newLang = e.detail.lang;
+ console.log(`Component notified: language changed to ${newLang}`);
+
+ // 组件特定的更新逻辑
+ // 例如:重新渲染图表标题、更新提示文本等
+});
+```
+
+**i18n框架增强(支持无刷新切换)**:
+```javascript
+// i18n.js中的switchLanguage方法
+async switchLanguage(lang) {
+ if (lang === this.currentLang) return;
+
+ try {
+ // 1. 保存到localStorage
+ localStorage.setItem('monitor-lang', lang);
+
+ // 2. 加载新语言包
+ this.currentLang = lang;
+ await this.loadLanguage(lang);
+
+ // 3. 应用翻译到所有静态元素
+ this.applyTranslations();
+
+ // 4. 更新语言选择器状态
+ this.updateLangSelector();
+
+ // 5. 触发事件(不刷新页面)
+ window.dispatchEvent(new CustomEvent('langchange', {
+ detail: { lang: lang }
+ }));
+
+ console.log(`[i18n] Language switched to: ${lang} (no reload)`);
+ } catch (error) {
+ console.error('[i18n] Language switch failed:', error);
+ }
+}
+```
+
+**无刷新切换优势**:
+- ✅ 保持页面滚动位置
+- ✅ 保持表单输入状态
+- ✅ 保持表格筛选条件
+- ✅ 保持分页状态
+- ✅ 用户体验流畅,无白屏闪烁
+
+---
+
+## 六、风险与对策
+
+### 6.1 风险清单
+
+| 风险 | 级别 | 影响 | 对策 |
+|------|------|------|------|
+| 后端错误消息硬编码 | 高 | 错误提示无法翻译 | 后端返回错误码+参数,前端统一翻译 |
+| 动态消息参数化 | 中 | 翻译不准确 | 使用i18n插值功能,三语言测试验证 |
+| Bootstrap弹窗动态生成 | 中 | 弹窗内容不更新 | 弹窗打开时重新应用翻译,语言切换事件监听 |
+| 状态枚举多处使用 | 中 | 改造遗漏 | 改造为getter动态翻译,无需手动刷新 |
+| 德语文本较长 | 中 | 布局溢出 | CSS增加文本自适应,德语专项布局测试 |
+| 三语言翻译质量 | 低 | 德语翻译不准确 | 使用专业翻译工具+人工校对 |
+| 工作量大 | 低 | 实施周期长 | 分阶段实施,P0优先,三语言并行翻译 |
+
+### 6.2 质量保证措施
+
+1. **代码审查**:每个阶段完成后进行代码审查
+2. **单元测试**:编写翻译函数单元测试
+3. **回归测试**:确保现有功能不受影响
+4. **性能测试**:验证语言包加载性能
+5. **用户验收**:邀请用户测试语言切换功能
+
+---
+
+## 七、翻译示例
+
+### 7.1 核心翻译键设计
+
+#### 7.1.1 简体中文(zh-CN.js)
+
+```javascript
+// zh-CN.js 扩展内容
+{
+ // ========== MDS模块 ==========
+
+ // 应用标题
+ 'mds.app.title': '数据清洗管理系统',
+ 'mds.app.guide': '操作指引',
+ 'mds.app.apiDoc': 'API文档',
+
+ // 导航菜单
+ 'mds.nav.material': '物料',
+ 'mds.nav.matVer': '产线版本',
+ 'mds.nav.workcenter': '工作中心',
+ 'mds.nav.matWc': '工艺路线',
+ 'mds.nav.bom': 'BOM',
+ 'mds.nav.mold': '模具',
+ 'mds.nav.matWcMold': '机台模具',
+
+ // 操作按钮
+ 'mds.action.import': '导入',
+ 'mds.action.validate': '校验',
+ 'mds.action.sync': '推送',
+ 'mds.action.query': '查询',
+ 'mds.action.reset': '重置',
+ 'mds.action.save': '保存',
+ 'mds.action.delete': '删除',
+ 'mds.action.cancel': '取消',
+ 'mds.action.confirm': '确定',
+
+ // 状态标签
+ 'mds.status.all': '全部',
+ 'mds.status.pending': '待处理',
+ 'mds.status.compliancePass': '初检通过',
+ 'mds.status.complianceError': '初检错误',
+ 'mds.status.relationPass': '联检通过',
+ 'mds.status.relationError': '联检错误',
+ 'mds.status.syncError': '推送失败',
+ 'mds.status.synced': '已推送',
+
+ // 校验相关
+ 'mds.validation.complete': '校验完成: 通过{pass}条,失败{fail}条',
+ 'mds.validation.noPending': '没有待处理的记录',
+ 'mds.validation.confirmStart': '缺失的字段值将自动填充为默认值,确定开始校验吗?',
+ 'mds.validation.processing': '校验中',
+ 'mds.validation.failed': '校验失败',
+ 'mds.validation.progress': '已处理 {current}/{total}',
+
+ // 推送相关
+ 'mds.sync.complete': '推送完成: {accounts}个账套, 成功{synced}条, 去重失败{dedup}条, 其他失败{failed}条',
+ 'mds.sync.noData': '没有【联合校验通过】或【同步失败】的记录可推送',
+ 'mds.sync.selectTarget': '请至少选择一个目标账套',
+ 'mds.sync.selectMode': '选择推送模式',
+ 'mds.sync.incremental': '增量推送',
+ 'mds.sync.refresh': '刷新推送',
+ 'mds.sync.confirmRefresh': '刷新推送将删除正式表所有数据,请谨慎操作!',
+
+ // 上传相关
+ 'mds.upload.success': '导入完成: 成功{inserted}条, 跳过{skipped}条',
+ 'mds.upload.invalidType': '请上传Excel或CSV文件',
+ 'mds.upload.noFile': '请先选择文件',
+ 'mds.upload.dragDrop': '点击或拖拽文件上传(支持 .xlsx, .xls, .csv)',
+
+ // 表格相关
+ 'mds.table.noData': '暂无数据',
+ 'mds.table.loading': '加载中...',
+ 'mds.table.selectAll': '全选',
+ 'mds.table.perPage': '条/页',
+ 'mds.table.total': '共 {count} 条',
+ 'mds.table.edit': '编辑',
+ 'mds.table.delete': '删除',
+ 'mds.table.export': '导出模板',
+
+ // 弹窗相关
+ 'mds.modal.confirm': '确认',
+ 'mds.modal.cancel': '取消',
+ 'mds.modal.close': '关闭',
+ 'mds.modal.importTitle': '导入Excel数据',
+ 'mds.modal.filterTitle': '精准筛选',
+ 'mds.modal.editTitle': '编辑记录',
+ 'mds.modal.validationRules': '校验规则',
+
+ // 错误消息
+ 'mds.error.queryFailed': '查询失败',
+ 'mds.error.uploadFailed': '上传失败',
+ 'mds.error.timeout': '请求超时',
+ 'mds.error.loadFailed': '加载失败,请重试'
+}
+```
+
+#### 7.1.2 英语(en-US.js)
+
+```javascript
+// en-US.js 扩展内容
+{
+ // ========== MDS Module ==========
+
+ // Application Title
+ 'mds.app.title': 'Data Staging Management System',
+ 'mds.app.guide': 'User Guide',
+ 'mds.app.apiDoc': 'API Documentation',
+
+ // Navigation Menu
+ 'mds.nav.material': 'Material',
+ 'mds.nav.matVer': 'Material Version',
+ 'mds.nav.workcenter': 'Work Center',
+ 'mds.nav.matWc': 'Routing',
+ 'mds.nav.bom': 'BOM',
+ 'mds.nav.mold': 'Mold',
+ 'mds.nav.matWcMold': 'Machine Mold',
+
+ // Action Buttons
+ 'mds.action.import': 'Import',
+ 'mds.action.validate': 'Validate',
+ 'mds.action.sync': 'Sync',
+ 'mds.action.query': 'Query',
+ 'mds.action.reset': 'Reset',
+ 'mds.action.save': 'Save',
+ 'mds.action.delete': 'Delete',
+ 'mds.action.cancel': 'Cancel',
+ 'mds.action.confirm': 'Confirm',
+
+ // Status Labels
+ 'mds.status.all': 'All',
+ 'mds.status.pending': 'Pending',
+ 'mds.status.compliancePass': 'Compliance Pass',
+ 'mds.status.complianceError': 'Compliance Error',
+ 'mds.status.relationPass': 'Relation Pass',
+ 'mds.status.relationError': 'Relation Error',
+ 'mds.status.syncError': 'Sync Failed',
+ 'mds.status.synced': 'Synced',
+
+ // Validation Related
+ 'mds.validation.complete': 'Validation: {pass} passed, {fail} failed',
+ 'mds.validation.noPending': 'No pending records',
+ 'mds.validation.confirmStart': 'Missing field values will be filled with defaults. Proceed?',
+ 'mds.validation.processing': 'Validating',
+ 'mds.validation.failed': 'Validation Failed',
+ 'mds.validation.progress': 'Processed {current}/{total}',
+
+ // Sync Related
+ 'mds.sync.complete': 'Sync complete: {accounts} accounts, {synced} synced, {dedup} dedup failed, {failed} other failed',
+ 'mds.sync.noData': 'No [Relation Pass] or [Sync Failed] records to sync',
+ 'mds.sync.selectTarget': 'Please select at least one target account',
+ 'mds.sync.selectMode': 'Select Sync Mode',
+ 'mds.sync.incremental': 'Incremental Sync',
+ 'mds.sync.refresh': 'Refresh Sync',
+ 'mds.sync.confirmRefresh': 'Refresh sync will delete all data in the main table. Proceed with caution!',
+
+ // Upload Related
+ 'mds.upload.success': 'Import complete: {inserted} inserted, {skipped} skipped',
+ 'mds.upload.invalidType': 'Please upload Excel or CSV file',
+ 'mds.upload.noFile': 'Please select a file first',
+ 'mds.upload.dragDrop': 'Click or drag file to upload (supports .xlsx, .xls, .csv)',
+
+ // Table Related
+ 'mds.table.noData': 'No data',
+ 'mds.table.loading': 'Loading...',
+ 'mds.table.selectAll': 'Select All',
+ 'mds.table.perPage': 'per page',
+ 'mds.table.total': 'Total {count} records',
+ 'mds.table.edit': 'Edit',
+ 'mds.table.delete': 'Delete',
+ 'mds.table.export': 'Export Template',
+
+ // Modal Related
+ 'mds.modal.confirm': 'Confirm',
+ 'mds.modal.cancel': 'Cancel',
+ 'mds.modal.close': 'Close',
+ 'mds.modal.importTitle': 'Import Excel Data',
+ 'mds.modal.filterTitle': 'Advanced Filter',
+ 'mds.modal.editTitle': 'Edit Record',
+ 'mds.modal.validationRules': 'Validation Rules',
+
+ // Error Messages
+ 'mds.error.queryFailed': 'Query Failed',
+ 'mds.error.uploadFailed': 'Upload Failed',
+ 'mds.error.timeout': 'Request Timeout',
+ 'mds.error.loadFailed': 'Load Failed, Please Retry'
+}
+```
+
+#### 7.1.3 德语(de-DE.js)
+
+```javascript
+// de-DE.js 扩展内容
+{
+ // ========== MDS-Modul ==========
+
+ // Anwendungstitel
+ 'mds.app.title': 'Daten-Staging-Verwaltungssystem',
+ 'mds.app.guide': 'Benutzerhandbuch',
+ 'mds.app.apiDoc': 'API-Dokumentation',
+
+ // Navigationsmenü
+ 'mds.nav.material': 'Material',
+ 'mds.nav.matVer': 'Materialversion',
+ 'mds.nav.workcenter': 'Arbeitsplatz',
+ 'mds.nav.matWc': 'Arbeitsplan',
+ 'mds.nav.bom': 'Stückliste',
+ 'mds.nav.mold': 'Werkzeug',
+ 'mds.nav.matWcMold': 'Maschinen-Werkzeug',
+
+ // Aktionsschaltflächen
+ 'mds.action.import': 'Importieren',
+ 'mds.action.validate': 'Validieren',
+ 'mds.action.sync': 'Synchronisieren',
+ 'mds.action.query': 'Abfragen',
+ 'mds.action.reset': 'Zurücksetzen',
+ 'mds.action.save': 'Speichern',
+ 'mds.action.delete': 'Löschen',
+ 'mds.action.cancel': 'Abbrechen',
+ 'mds.action.confirm': 'Bestätigen',
+
+ // Statusbezeichnungen
+ 'mds.status.all': 'Alle',
+ 'mds.status.pending': 'Ausstehend',
+ 'mds.status.compliancePass': 'Compliance bestanden',
+ 'mds.status.complianceError': 'Compliance fehlerhaft',
+ 'mds.status.relationPass': 'Relation bestanden',
+ 'mds.status.relationError': 'Relation fehlerhaft',
+ 'mds.status.syncError': 'Sync fehlgeschlagen',
+ 'mds.status.synced': 'Synchronisiert',
+
+ // Validierungsbezogen
+ 'mds.validation.complete': 'Validierung: {pass} bestanden, {fail} fehlgeschlagen',
+ 'mds.validation.noPending': 'Keine ausstehenden Datensätze',
+ 'mds.validation.confirmStart': 'Fehlende Feldwerte werden mit Standardwerten gefüllt. Fortfahren?',
+ 'mds.validation.processing': 'Validierung läuft',
+ 'mds.validation.failed': 'Validierung fehlgeschlagen',
+ 'mds.validation.progress': 'Verarbeitet {current}/{total}',
+
+ // Synchronisationsbezogen
+ 'mds.sync.complete': 'Sync abgeschlossen: {accounts} Konten, {synced} synchronisiert, {dedup} Dedup fehlgeschlagen, {failed} sonstige fehlgeschlagen',
+ 'mds.sync.noData': 'Keine [Relation bestanden] oder [Sync fehlgeschlagen] Datensätze zum Synchronisieren',
+ 'mds.sync.selectTarget': 'Bitte mindestens ein Zielkonto auswählen',
+ 'mds.sync.selectMode': 'Synchronisationsmodus wählen',
+ 'mds.sync.incremental': 'Inkrementelle Synchronisation',
+ 'mds.sync.refresh': 'Aktualisierungs-Sync',
+ 'mds.sync.confirmRefresh': 'Aktualisierungs-Sync löscht alle Daten in der Haupttabelle. Mit Vorsicht fortfahren!',
+
+ // Upload-bezogen
+ 'mds.upload.success': 'Import abgeschlossen: {inserted} eingefügt, {skipped} übersprungen',
+ 'mds.upload.invalidType': 'Bitte Excel- oder CSV-Datei hochladen',
+ 'mds.upload.noFile': 'Bitte zuerst eine Datei auswählen',
+ 'mds.upload.dragDrop': 'Klicken oder Datei ziehen (unterstützt .xlsx, .xls, .csv)',
+
+ // Tabellenbezogen
+ 'mds.table.noData': 'Keine Daten',
+ 'mds.table.loading': 'Laden...',
+ 'mds.table.selectAll': 'Alle auswählen',
+ 'mds.table.perPage': 'pro Seite',
+ 'mds.table.total': 'Gesamt {count} Datensätze',
+ 'mds.table.edit': 'Bearbeiten',
+ 'mds.table.delete': 'Löschen',
+ 'mds.table.export': 'Vorlage exportieren',
+
+ // Modal-bezogen
+ 'mds.modal.confirm': 'Bestätigen',
+ 'mds.modal.cancel': 'Abbrechen',
+ 'mds.modal.close': 'Schließen',
+ 'mds.modal.importTitle': 'Excel-Daten importieren',
+ 'mds.modal.filterTitle': 'Erweiterter Filter',
+ 'mds.modal.editTitle': 'Datensatz bearbeiten',
+ 'mds.modal.validationRules': 'Validierungsregeln',
+
+ // Fehlermeldungen
+ 'mds.error.queryFailed': 'Abfrage fehlgeschlagen',
+ 'mds.error.uploadFailed': 'Upload fehlgeschlagen',
+ 'mds.error.timeout': 'Anfrage-Timeout',
+ 'mds.error.loadFailed': 'Laden fehlgeschlagen, bitte erneut versuchen'
+}
+```
+
+---
+
+## 八、验收标准
+
+### 8.1 功能验收
+
+- [ ] 所有页面支持语言切换(简体中文、英语、德语)
+- [ ] 三语言文本正确显示
+- [ ] 状态枚举动态翻译
+- [ ] 操作按钮文本翻译
+- [ ] 弹窗内容翻译
+- [ ] 表单验证消息翻译
+- [ ] 错误提示翻译(支持参数化)
+- [ ] 表格空状态翻译
+- [ ] 分页控件翻译
+- [ ] 无刷新语言切换(保持页面状态)
+
+### 8.2 性能验收
+
+- [ ] 语言包加载时间 < 300ms(三语言包总计约120KB)
+- [ ] 语言切换响应时间 < 100ms(无刷新)
+- [ ] 内存占用增加 < 8MB(三语言包加载)
+- [ ] 无刷新切换无白屏闪烁
+
+### 8.3 兼容性验收
+
+- [ ] Chrome浏览器兼容
+- [ ] Firefox浏览器兼容
+- [ ] Edge浏览器兼容
+- [ ] Safari浏览器兼容(如有条件)
+- [ ] 德语文本在不同浏览器下正确显示(无乱码)
+
+### 8.4 文档验收
+
+- [ ] 实施文档完整
+- [ ] 翻译键命名规范
+- [ ] 代码注释清晰
+
+---
+
+## 九、后续维护
+
+### 9.1 新增翻译流程
+
+1. 在代码中使用翻译键:`i18n.t('mds.new.key')`
+2. 在所有语言包中添加对应翻译
+3. 更新翻译键文档
+4. 测试验证
+
+### 9.2 翻译质量保证
+
+- 使用统一的翻译键命名规范
+- 定期检查未使用的翻译键
+- 保持翻译文本简洁准确
+
+### 9.3 扩展其他语言
+
+如需支持其他语言(如日语、法语等):
+1. 创建新语言包文件(如`ja-JP.js`)
+2. 复制`zh-CN.js`作为模板
+3. 翻译所有文本
+4. 在语言选择器中添加选项
+
+---
+
+## 十、附录
+
+### 10.1 相关文档
+
+- [Monitor模块国际化实施计划](./Monitor-i18n-Implementation-Plan.md)
+- [Monitor模块国际化测试报告](./Monitor-i18n-Test-Report.md)
+- [AGENTS.md 开发指南](../../AGENTS.md)
+
+### 10.2 参考资料
+
+- i18next官方文档:https://www.i18next.com/
+- Bootstrap国际化最佳实践
+- FastAPI国际化方案
+
+### 10.3 变更历史
+
+| 版本 | 日期 | 变更内容 | 作者 |
+|------|------|----------|------|
+| v1.0 | 2026-05-24 | 初版创建 | CodeArts |
+| v1.1 | 2026-05-24 | 优化技术方案:支持简体中文/英语/德语三语言、无刷新语言切换、后端错误码+参数化处理 | CodeArts |
+
+---
+
+**文档状态**:待评审
+**下一步**:评审通过后开始Phase 1实施
+
+---
+
+## 十一、三语言支持补充说明
+
+### 11.1 语言包文件规划
+
+基于Monitor模块已实现的三语言支持,MDS模块将复用相同的语言包文件:
+
+```
+static/lib/i18n/
+├── i18n.js # 核心框架(已存在,无需修改)
+├── zh-CN.js # 简体中文(扩展MDS命名空间)
+├── en-US.js # 英语(扩展MDS命名空间)
+└── de-DE.js # 德语(扩展MDS命名空间)
+```
+
+### 11.2 德语翻译注意事项
+
+**1. 文本长度**
+德语文本通常比中文和英文长20%-40%,需特别注意:
+- 按钮文本:如"Validieren"(德语)vs "Validate"(英语)
+- 表格列名:需预留足够的列宽
+- 错误提示:可能需要自动换行或省略
+
+**2. 特殊字符**
+德语包含特殊字符(ä, ö, ü, ß),需确保:
+- HTML文件使用UTF-8编码
+- 语言包文件保存为UTF-8无BOM格式
+- 服务器响应头正确设置Content-Type
+
+**3. 复合词**
+德语倾向使用复合词,翻译时需考虑:
+- 可读性:适当使用连字符提高可读性
+- 一致性:统一术语翻译规则
+
+**示例对比**:
+| 中文 | 英语 | 德语 | 长度对比 |
+|------|------|------|----------|
+| 数据清洗管理系统 | Data Staging Management System | Daten-Staging-Verwaltungssystem | 德语最长 |
+| 校验 | Validate | Validieren | 德语较长 |
+| 待处理 | Pending | Ausstehend | 德语较长 |
+
+### 11.3 三语言翻译流程
+
+**流程图**:
+```
+1. 提取中文文本 → 2. 编写中文翻译键
+ ↓
+3. 翻译为英语 → 4. 校验英语翻译准确性
+ ↓
+5. 翻译为德语 → 6. 校验德语翻译准确性
+ ↓
+7. 三语言对比测试 → 8. 布局适配验证
+```
+
+**翻译工具建议**:
+- DeepL(德语翻译质量较高)
+- Google Translate
+- 人工校对(关键术语和用户界面文本)
+
+### 11.4 三语言切换用户体验
+
+**无刷新切换优势**:
+- ✅ 保持表格筛选条件
+- ✅ 保持分页状态
+- ✅ 保持表单输入
+- ✅ 保持滚动位置
+- ✅ 无白屏闪烁,体验流畅
+
+**切换流程**:
+```
+用户选择语言 → i18n.switchLanguage(lang)
+ ↓
+更新localStorage → 加载新语言包
+ ↓
+应用翻译到DOM → 更新动态元素
+ ↓
+触发语言切换事件 → 组件响应更新
+ ↓
+完成(无刷新)
+```
+
+### 11.5 与Monitor模块协同
+
+**共享机制**:
+- 同一个i18n实例
+- 同一个语言选择器
+- 同一个localStorage存储键(`monitor-lang`)
+
+**切换同步**:
+- 用户在Monitor页面切换语言 → MDS页面同时生效
+- 用户在MDS页面切换语言 → Monitor页面同时生效
+- 无需重复切换,全局生效
+
+**命名空间隔离**:
+```javascript
+// Monitor模块使用 monitor. 前缀
+'monitor.page.title': 'MyAPI System Monitor'
+
+// MDS模块使用 mds. 前缀
+'mds.app.title': 'Data Staging Management System'
+
+// 避免键冲突,清晰区分
+```
+
+---
+
+## 十二、实施检查清单(三语言)
+
+### 12.1 Phase 1 检查清单
+
+- [ ] zh-CN.js添加MDS命名空间(约750条)
+- [ ] en-US.js添加MDS命名空间(约750条)
+- [ ] de-DE.js添加MDS命名空间(约750条)
+- [ ] 三语言翻译项数量一致
+- [ ] 状态枚举getter动态翻译测试通过
+- [ ] 三语言切换无报错
+- [ ] 德语文本无乱码
+
+### 12.2 Phase 2 检查清单
+
+- [ ] HTML三语言文本正确显示
+- [ ] 弹窗三语言翻译测试通过
+- [ ] 德语文本无溢出
+- [ ] 导航菜单三语言正确
+
+### 12.3 Phase 3 检查清单
+
+- [ ] 错误码+参数机制测试通过
+- [ ] 三语言错误提示正确
+- [ ] 表格三语言显示正确
+- [ ] 分页控件三语言正确
+
+### 12.4 Phase 4 检查清单
+
+- [ ] 所有页面三语言完整测试
+- [ ] 无刷新切换测试通过
+- [ ] 德语布局专项测试通过
+- [ ] 与Monitor模块协同测试通过
+- [ ] 性能验收通过
+- [ ] 浏览器兼容性测试通过
+
+---
+
+**补充说明结束**
diff --git a/docs/spec/MDS-i18n-Task-Plan.md b/docs/spec/MDS-i18n-Task-Plan.md
new file mode 100644
index 0000000..526781e
--- /dev/null
+++ b/docs/spec/MDS-i18n-Task-Plan.md
@@ -0,0 +1,1926 @@
+# MDS模块国际化编码任务规划
+
+**文档类型**: 实施任务清单
+**创建日期**: 2026-05-24
+**版本**: v1.0
+**状态**: 待执行
+
+---
+
+## 一、任务总览
+
+### 1.1 项目信息
+
+- **项目名称**: MDS模块国际化改造
+- **目标**: 支持简体中文、英语、德语三语言切换
+- **总工作量**: 19人天(约4周)
+- **实施方式**: 分4个阶段递进实施
+
+### 1.2 任务统计
+
+| 阶段 | 任务数 | 工作量 | 优先级 |
+|------|--------|--------|--------|
+| Phase 1: 基础准备 | 14个任务 | 4天 | P0+P1 |
+| Phase 2: 导航与弹窗 | 16个任务 | 5天 | P2+P3 |
+| Phase 3: 错误与表格 | 18个任务 | 6天 | P4+P5 |
+| Phase 4: 完成与测试 | 14个任务 | 4天 | P6 |
+| **合计** | **62个任务** | **19天** | |
+
+### 1.3 核心目标
+
+- [x] 复用Monitor模块的i18n框架
+- [x] 扩展现有语言包(增加MDS命名空间)
+- [x] 实现无刷新语言切换
+- [x] 状态枚举动态翻译
+- [x] 后端错误码+参数化处理
+- [x] 三语言翻译质量保证
+
+---
+
+## 二、Phase 1任务清单(基础准备,4天)
+
+### 2.1 语言包创建(Day 1)
+
+#### 任务1.1: 扩展zh-CN.js语言包
+
+**任务描述**: 在现有中文语言包中添加MDS模块命名空间
+
+**涉及文件**:
+- `static/lib/i18n/zh-CN.js`
+
+**具体工作**:
+- 提取P0级文本(状态标签,28处): mds.status.*
+- 提取P1级文本(操作按钮,45处): mds.action.*
+- 编写翻译键,遵循命名规范
+- 添加注释分隔模块
+
+**预期产出**:
+```javascript
+// zh-CN.js 新增内容
+{
+ // ========== MDS模块 ==========
+ 'mds.status.pending': '待处理',
+ 'mds.status.compliancePass': '初检通过',
+ 'mds.status.complianceError': '初检错误',
+ 'mds.status.relationPass': '联检通过',
+ 'mds.status.relationError': '联检错误',
+ 'mds.status.syncError': '推送失败',
+ 'mds.status.synced': '已推送',
+
+ 'mds.action.import': '导入',
+ 'mds.action.validate': '校验',
+ 'mds.action.sync': '推送',
+ 'mds.action.query': '查询',
+ 'mds.action.reset': '重置',
+ // ... 共约73条
+}
+```
+
+**验收标准**:
+- [ ] 翻译键数量符合预期(约73条)
+- [ ] 键命名规范统一(mds.status.*, mds.action.*)
+- [ ] 注释清晰分隔模块
+- [ ] 无重复键定义
+
+**工作量**: 1天
+
+---
+
+#### 任务1.2: 扩展en-US.js语言包
+
+**任务描述**: 在现有英文语言包中添加MDS模块翻译
+
+**涉及文件**:
+- `static/lib/i18n/en-US.js`
+
+**具体工作**:
+- 将zh-CN.js中的MDS翻译键翻译为英文
+- 对应翻译键与zh-CN.js保持一致
+- 使用专业术语(如Compliance Pass, Sync)
+
+**预期产出**:
+```javascript
+// en-US.js 新增内容
+{
+ // ========== MDS Module ==========
+ 'mds.status.pending': 'Pending',
+ 'mds.status.compliancePass': 'Compliance Pass',
+ 'mds.status.complianceError': 'Compliance Error',
+ 'mds.status.relationPass': 'Relation Pass',
+ 'mds.status.relationError': 'Relation Error',
+ 'mds.status.syncError': 'Sync Failed',
+ 'mds.status.synced': 'Synced',
+
+ 'mds.action.import': 'Import',
+ 'mds.action.validate': 'Validate',
+ 'mds.action.sync': 'Sync',
+ // ... 共约73条
+}
+```
+
+**验收标准**:
+- [ ] 翻译键数量与zh-CN.js一致
+- [ ] 英文翻译准确、专业
+- [ ] 键顺序与zh-CN.js对应
+
+**工作量**: 0.5天
+
+---
+
+#### 任务1.3: 扩展de-DE.js语言包
+
+**任务描述**: 在现有德语语言包中添加MDS模块翻译
+
+**涉及文件**:
+- `static/lib/i18n/de-DE.js`
+
+**具体工作**:
+- 将zh-CN.js中的MDS翻译键翻译为德语
+- 注意德语特殊字符(ä, ö, ü, ß)
+- 关注德语文本长度(可能比英文长20%-40%)
+
+**预期产出**:
+```javascript
+// de-DE.js 新增内容
+{
+ // ========== MDS-Modul ==========
+ 'mds.status.pending': 'Ausstehend',
+ 'mds.status.compliancePass': 'Compliance bestanden',
+ 'mds.status.complianceError': 'Compliance fehlerhaft',
+ 'mds.status.relationPass': 'Relation bestanden',
+ 'mds.status.relationError': 'Relation fehlerhaft',
+ 'mds.status.syncError': 'Sync fehlgeschlagen',
+ 'mds.status.synced': 'Synchronisiert',
+
+ 'mds.action.import': 'Importieren',
+ 'mds.action.validate': 'Validieren',
+ 'mds.action.sync': 'Synchronisieren',
+ // ... 共约73条
+}
+```
+
+**验收标准**:
+- [ ] 翻译键数量与zh-CN.js一致
+- [ ] 德语翻译准确,使用专业术语
+- [ ] 特殊字符正确编码(UTF-8)
+- [ ] 文件保存为UTF-8无BOM格式
+
+**工作量**: 0.5天
+
+---
+
+### 2.2 状态枚举改造(Day 2)
+
+#### 任务1.4: 改造STAGING_STATUS枚举
+
+**任务描述**: 将common.js中的STAGING_STATUS改造为动态翻译
+
+**涉及文件**:
+- `static/mds/js/common.js`
+
+**具体工作**:
+- 将label字段改为labelKey
+- 添加getter动态获取翻译
+- 确保i18n未初始化时的回退机制
+
+**改造前**:
+```javascript
+const STAGING_STATUS = {
+ PENDING: {
+ value: 'pending',
+ label: '待处理',
+ color: '#ff9300',
+ icon: '⏳'
+ }
+};
+```
+
+**改造后**:
+```javascript
+const STAGING_STATUS = {
+ PENDING: {
+ value: 'pending',
+ labelKey: 'mds.status.pending',
+ color: '#ff9300',
+ icon: '⏳',
+ get label() {
+ return typeof i18n !== 'undefined' ?
+ i18n.t(this.labelKey) : '待处理';
+ }
+ }
+};
+```
+
+**验收标准**:
+- [ ] 所有状态枚举都使用getter
+- [ ] i18n未初始化时显示中文回退
+- [ ] 语言切换后状态标签自动更新
+- [ ] 控制台无报错
+
+**工作量**: 0.5天
+
+---
+
+#### 任务1.5: 改造status-card.js组件
+
+**任务描述**: 改造状态统计卡片组件,支持动态翻译
+
+**涉及文件**:
+- `static/mds/js/status-card.js`
+
+**具体工作**:
+- 渲染状态卡片时使用STAGING_STATUS.label
+- 监听languageChanged事件,刷新卡片
+- 确保切换语言后数据正确显示
+
+**关键代码**:
+```javascript
+// 渲染状态卡片
+function renderStatusCards() {
+ const statuses = Object.values(STAGING_STATUS);
+ statuses.forEach(status => {
+ // label自动动态翻译
+ const label = status.label;
+ // ...
+ });
+}
+
+// 监听语言切换事件
+window.addEventListener('languageChanged', () => {
+ renderStatusCards();
+});
+```
+
+**验收标准**:
+- [ ] 状态卡片显示三语言文本
+- [ ] 语言切换后卡片自动刷新
+- [ ] 数据统计正确
+
+**工作量**: 0.5天
+
+---
+
+#### 任务1.6: 改造其他枚举对象
+
+**任务描述**: 改造其他包含中文label的枚举对象
+
+**涉及文件**:
+- `static/mds/js/common.js`
+
+**具体工作**:
+- 检查其他枚举对象(如BUTTON_TEXT, ERROR_MSG等)
+- 统一改造为labelKey + getter模式
+- 确保所有硬编码文本都被翻译键替换
+
+**验收标准**:
+- [ ] 所有枚举都支持动态翻译
+- [ ] 无遗漏的硬编码中文文本
+- [ ] 三语言切换测试通过
+
+**工作量**: 0.5天
+
+---
+
+### 2.3 验证与测试(Day 3-4)
+
+#### 任务1.7: 创建语言切换UI
+
+**任务描述**: 在导航栏添加三语言选择器
+
+**涉及文件**:
+- `static/mds/index.html`
+
+**具体工作**:
+- 添加语言选择下拉框
+- 复用Monitor的语言选择器样式
+- 绑定切换事件处理函数
+
+**预期产出**:
+```html
+
+```
+
+**验收标准**:
+- [ ] 语言选择器正确显示
+- [ ] 样式与Monitor一致
+- [ ] 三语言图标正确
+
+**工作量**: 0.5天
+
+---
+
+#### 任务1.8: 实现无刷新语言切换
+
+**任务描述**: 实现语言切换功能,无刷新更新页面文本
+
+**涉及文件**:
+- `static/mds/js/mds-page-controller.js` (新增函数)
+
+**具体工作**:
+- 实现switchLanguage函数
+- 调用i18n.switchLanguage切换语言
+- 更新动态元素(表格、卡片、弹窗)
+- 触发languageChanged事件
+
+**关键代码**:
+```javascript
+async function switchLanguage(lang) {
+ if (typeof i18n === 'undefined') {
+ console.warn('i18n not initialized');
+ return;
+ }
+
+ await i18n.switchLanguage(lang);
+ updateDynamicElements();
+
+ window.dispatchEvent(new CustomEvent('languageChanged', {
+ detail: { lang: lang }
+ }));
+}
+
+function updateDynamicElements() {
+ // 更新状态卡片
+ if (window.statusCardInstance) {
+ window.statusCardInstance.refresh();
+ }
+
+ // 更新表格
+ if (window.dataTableInstance) {
+ window.dataTableInstance.refresh();
+ }
+
+ // 更新已打开的模态框
+ const modals = document.querySelectorAll('.modal.show');
+ modals.forEach(modal => {
+ modal.querySelectorAll('[data-i18n]').forEach(el => {
+ const key = el.getAttribute('data-i18n');
+ el.textContent = i18n.t(key);
+ });
+ });
+}
+```
+
+**验收标准**:
+- [ ] 语言切换无页面刷新
+- [ ] 保持表格筛选条件
+- [ ] 保持分页状态
+- [ ] 保持滚动位置
+- [ ] 无白屏闪烁
+
+**工作量**: 1天
+
+---
+
+#### 任务1.9: 测试状态枚举翻译
+
+**任务描述**: 测试状态枚举在三种语言下的翻译
+
+**涉及文件**:
+- 所有使用STAGING_STATUS的文件
+
+**具体工作**:
+- 切换到中文,验证状态标签显示"待处理"等
+- 切换到英文,验证显示"Pending"等
+- 切换到德语,验证显示"Ausstehend"等
+- 测试语言切换后的自动更新
+
+**验收标准**:
+- [ ] 中英德三语言翻译正确
+- [ ] 语言切换后立即更新
+- [ ] 控制台无报错
+
+**工作量**: 0.5天
+
+---
+
+#### 任务1.10: 测试操作按钮翻译
+
+**任务描述**: 测试操作按钮在三语言下的翻译
+
+**具体工作**:
+- 测试"导入"、"校验"、"推送"等按钮
+- 验证三语言显示
+- 测试德语长文本布局
+
+**验收标准**:
+- [ ] 所有按钮文本翻译正确
+- [ ] 德语文本无溢出
+- [ ] 按钮样式正常
+
+**工作量**: 0.5天
+
+---
+
+#### 任务1.11: 测试无刷新切换
+
+**任务描述**: 测试无刷新语言切换功能
+
+**具体工作**:
+- 在表格筛选状态下切换语言
+- 在分页状态下切换语言
+- 在表单输入状态下切换语言
+- 验证状态保持
+
+**验收标准**:
+- [ ] 筛选条件不丢失
+- [ ] 分页状态保持
+- [ ] 表单输入不丢失
+- [ ] 滚动位置保持
+
+**工作量**: 0.5天
+
+---
+
+#### 任务1.12: 测试德语布局适配
+
+**任务描述**: 测试德语长文本的布局适配
+
+**具体工作**:
+- 检查所有按钮是否溢出
+- 检查表格列宽是否足够
+- 检查弹窗是否显示完整
+- 必要时调整CSS样式
+
+**验收标准**:
+- [ ] 无文本溢出
+- [ ] 无布局错乱
+- [ ] 德语特殊字符正确显示
+
+**工作量**: 0.5天
+
+---
+
+#### 任务1.13: 编写Phase 1测试报告
+
+**任务描述**: 记录Phase 1的测试结果
+
+**涉及文件**:
+- `docs/spec/MDS-i18n-Phase1-Test-Report.md` (新建)
+
+**具体工作**:
+- 记录测试用例
+- 记录测试结果
+- 记录发现的问题
+- 记录解决方案
+
+**验收标准**:
+- [ ] 测试报告完整
+- [ ] 所有问题已解决
+
+**工作量**: 0.5天
+
+---
+
+#### 任务1.14: Phase 1总结与评审
+
+**任务描述**: 总结Phase 1工作,进行代码评审
+
+**具体工作**:
+- 检查代码质量
+- 检查翻译键命名规范
+- 检查三语言包一致性
+- 进行代码评审
+
+**验收标准**:
+- [ ] 代码质量合格
+- [ ] 翻译键规范统一
+- [ ] 三语言包键数量一致
+- [ ] 评审通过
+
+**工作量**: 0.5天
+
+---
+
+## 三、Phase 2任务清单(导航与弹窗,5天)
+
+### 3.1 HTML页面改造(Day 5-6)
+
+#### 任务2.1: 改造index.html
+
+**任务描述**: 为index.html添加data-i18n属性
+
+**涉及文件**:
+- `static/mds/index.html`
+
+**具体工作**:
+- 提取所有硬编码中文文本
+- 为文本元素添加data-i18n属性
+- 提取P2级文本(导航菜单,14处)
+
+**改造示例**:
+```html
+
+
数据清洗管理系统
+
+
+数据清洗管理系统
+```
+
+**预期产出**:
+- 导航菜单翻译: mds.nav.material, mds.nav.bom等
+- 全局操作按钮翻译
+- 卡片标题翻译
+
+**验收标准**:
+- [ ] 所有中文文本都添加data-i18n
+- [ ] 翻译键命名规范
+- [ ] 页面结构不变
+
+**工作量**: 1天
+
+---
+
+#### 任务2.2: 改造template.html
+
+**任务描述**: 为数据操作页面模板添加data-i18n属性
+
+**涉及文件**:
+- `static/mds/pages/template.html`
+
+**具体工作**:
+- 提取所有硬编码中文文本
+- 添加data-i18n属性
+- 包括表格、筛选框、分页控件等
+
+**改造示例**:
+```html
+
+
+
+
+
+```
+
+**验收标准**:
+- [ ] 所有文本元素都有data-i18n
+- [ ] 动态生成的内容预留翻译钩子
+- [ ] 页面功能不变
+
+**工作量**: 1天
+
+---
+
+#### 任务2.3: 改造user-guide.html
+
+**任务描述**: 为操作指引页面添加data-i18n属性
+
+**涉及文件**:
+- `static/mds/user-guide.html`
+
+**具体工作**:
+- 提取操作指引文本
+- 添加data-i18n属性
+- 翻译指引说明
+
+**验收标准**:
+- [ ] 所有文本都有翻译
+- [ ] 三语言显示正确
+- [ ] 布局不变
+
+**工作量**: 0.5天
+
+---
+
+#### 任务2.4: 提取P2级翻译键
+
+**任务描述**: 提取导航菜单等P2级文本的翻译键
+
+**涉及文件**:
+- `static/lib/i18n/zh-CN.js`
+- `static/lib/i18n/en-US.js`
+- `static/lib/i18n/de-DE.js`
+
+**具体工作**:
+- 提取导航菜单文本(14处)
+- 添加到三语言包
+- 编写翻译
+
+**预期产出**:
+```javascript
+// zh-CN.js
+'mds.nav.material': '物料',
+'mds.nav.matVer': '产线版本',
+'mds.nav.workcenter': '工作中心',
+'mds.nav.matWc': '工艺路线',
+'mds.nav.bom': 'BOM',
+'mds.nav.mold': '模具',
+'mds.nav.matWcMold': '机台模具',
+```
+
+**验收标准**:
+- [ ] 翻译键数量正确(约14处)
+- [ ] 三语言翻译准确
+
+**工作量**: 0.5天
+
+---
+
+#### 任务2.5: 测试HTML页面翻译
+
+**任务描述**: 测试三个HTML页面的翻译效果
+
+**具体工作**:
+- 测试index.html三语言显示
+- 测试template.html三语言显示
+- 测试user-guide.html三语言显示
+- 测试语言切换功能
+
+**验收标准**:
+- [ ] 所有页面文本正确翻译
+- [ ] 语言切换后立即更新
+- [ ] 无布局错乱
+
+**工作量**: 0.5天
+
+---
+
+### 3.2 弹窗组件改造(Day 7-9)
+
+#### 任务2.6: 改造modal-manager.js基础函数
+
+**任务描述**: 改造模态框管理器的基础函数
+
+**涉及文件**:
+- `static/mds/js/modal-manager.js`
+
+**具体工作**:
+- 改造showConfirm函数,支持翻译键
+- 改造showAlert函数
+- 改造showPrompt函数
+- 确保弹窗标题、内容支持翻译
+
+**改造示例**:
+```javascript
+// 改造前
+function showConfirm(title, message, onConfirm) {
+ // ...
+}
+
+// 改造后
+function showConfirm(titleKey, messageKey, onConfirm, params = {}) {
+ const title = typeof i18n !== 'undefined' ?
+ i18n.t(titleKey) : titleKey;
+ const message = typeof i18n !== 'undefined' ?
+ i18n.t(messageKey, params) : messageKey;
+ // ...
+}
+```
+
+**验收标准**:
+- [ ] 所有弹窗函数支持翻译键
+- [ ] 支持参数化翻译
+- [ ] 控制台无报错
+
+**工作量**: 1天
+
+---
+
+#### 任务2.7: 改造导入弹窗
+
+**任务描述**: 改造Excel导入弹窗的翻译
+
+**涉及文件**:
+- `static/mds/js/modal-manager.js`
+- `static/lib/i18n/zh-CN.js`等
+
+**具体工作**:
+- 改造弹窗标题翻译
+- 改造拖拽提示翻译
+- 改造按钮翻译
+- 提取P3级弹窗文本
+
+**预期产出**:
+```javascript
+// 翻译键
+'mds.modal.importTitle': '导入Excel数据',
+'mds.upload.dragDrop': '点击或拖拽文件上传(支持 .xlsx, .xls, .csv)',
+'mds.modal.confirm': '确认',
+'mds.modal.cancel': '取消',
+```
+
+**验收标准**:
+- [ ] 弹窗标题翻译正确
+- [ ] 拖拽提示翻译正确
+- [ ] 按钮翻译正确
+
+**工作量**: 1天
+
+---
+
+#### 任务2.8: 改造筛选弹窗
+
+**任务描述**: 改造精准筛选弹窗的翻译
+
+**涉及文件**:
+- `static/mds/js/modal-manager.js`
+
+**具体工作**:
+- 改造筛选弹窗标题
+- 改造筛选条件文本
+- 改造按钮文本
+
+**验收标准**:
+- [ ] 弹窗内容翻译正确
+- [ ] 三语言显示正确
+
+**工作量**: 0.5天
+
+---
+
+#### 任务2.9: 改造编辑弹窗
+
+**任务描述**: 改造记录编辑弹窗的翻译
+
+**涉及文件**:
+- `static/mds/js/form-renderer.js`
+
+**具体工作**:
+- 改造form-renderer.js
+- 改造表单标签翻译
+- 改造表单验证消息翻译
+- 改造按钮文本
+
+**验收标准**:
+- [ ] 表单标签翻译正确
+- [ ] 验证消息翻译正确
+- [ ] 三语言显示正确
+
+**工作量**: 1天
+
+---
+
+#### 任务2.10: 提取P3级翻译键
+
+**任务描述**: 提取弹窗相关文本的翻译键(约120处)
+
+**涉及文件**:
+- `static/lib/i18n/zh-CN.js`
+- `static/lib/i18n/en-US.js`
+- `static/lib/i18n/de-DE.js`
+
+**具体工作**:
+- 提取所有弹窗文本
+- 编写三语言翻译
+- 添加到语言包
+
+**验收标准**:
+- [ ] 翻译键数量正确(约120处)
+- [ ] 三语言翻译准确
+- [ ] 键命名规范统一
+
+**工作量**: 1天
+
+---
+
+#### 任务2.11: 实现弹窗语言切换监听
+
+**任务描述**: 实现弹窗在语言切换时的动态更新
+
+**涉及文件**:
+- `static/mds/js/modal-manager.js`
+
+**具体工作**:
+- 监听languageChanged事件
+- 更新已打开弹窗的文本
+- 确保切换语言后弹窗内容更新
+
+**关键代码**:
+```javascript
+window.addEventListener('languageChanged', () => {
+ const modals = document.querySelectorAll('.modal.show');
+ modals.forEach(modal => {
+ modal.querySelectorAll('[data-i18n]').forEach(el => {
+ const key = el.getAttribute('data-i18n');
+ el.textContent = i18n.t(key);
+ });
+ });
+});
+```
+
+**验收标准**:
+- [ ] 语言切换后弹窗内容更新
+- [ ] 不关闭弹窗
+- [ ] 数据不丢失
+
+**工作量**: 0.5天
+
+---
+
+#### 任务2.12: 测试弹窗翻译
+
+**任务描述**: 测试所有弹窗的翻译效果
+
+**具体工作**:
+- 测试导入弹窗三语言显示
+- 测试筛选弹窗三语言显示
+- 测试编辑弹窗三语言显示
+- 测试确认弹窗三语言显示
+
+**验收标准**:
+- [ ] 所有弹窗翻译正确
+- [ ] 语言切换后弹窗更新
+- [ ] 功能正常
+
+**工作量**: 0.5天
+
+---
+
+#### 任务2.13: 测试德语弹窗布局
+
+**任务描述**: 测试德语环境下弹窗的布局
+
+**具体工作**:
+- 检查弹窗宽度是否足够
+- 检查按钮文本是否溢出
+- 必要时调整弹窗样式
+
+**验收标准**:
+- [ ] 无文本溢出
+- [ ] 无布局错乱
+- [ ] 弹窗美观
+
+**工作量**: 0.5天
+
+---
+
+#### 任务2.14: 编写Phase 2测试报告
+
+**任务描述**: 记录Phase 2的测试结果
+
+**涉及文件**:
+- `docs/spec/MDS-i18n-Phase2-Test-Report.md` (新建)
+
+**验收标准**:
+- [ ] 测试报告完整
+- [ ] 所有问题已解决
+
+**工作量**: 0.5天
+
+---
+
+#### 任务2.15: Phase 2总结与评审
+
+**任务描述**: 总结Phase 2工作,进行代码评审
+
+**验收标准**:
+- [ ] 代码质量合格
+- [ ] 翻译键规范统一
+- [ ] 评审通过
+
+**工作量**: 0.5天
+
+---
+
+#### 任务2.16: 提交Phase 2代码
+
+**任务描述**: 提交Phase 2的代码变更
+
+**具体工作**:
+- 检查代码变更
+- 编写提交信息
+- 提交代码
+
+**验收标准**:
+- [ ] 代码提交成功
+- [ ] 提交信息清晰
+
+**工作量**: 0.5天
+
+---
+
+## 四、Phase 3任务清单(错误与表格,6天)
+
+### 4.1 错误消息处理(Day 10-11)
+
+#### 任务3.1: 设计错误码映射表
+
+**任务描述**: 设计后端错误码到翻译键的映射表
+
+**涉及文件**:
+- `static/mds/js/common.js` (新增ERROR_CODE_MAP)
+
+**具体工作**:
+- 定义错误码常量
+- 创建错误码到翻译键的映射
+- 定义参数化错误模板
+
+**预期产出**:
+```javascript
+// 错误码常量
+const ERROR_CODES = {
+ DUPLICATE_KEY: 'DUPLICATE_KEY',
+ INVALID_FORMAT: 'INVALID_FORMAT',
+ FOREIGN_KEY_VIOLATION: 'FOREIGN_KEY_VIOLATION',
+ VALIDATION_FAILED: 'VALIDATION_FAILED'
+};
+
+// 错误码到翻译键的映射
+const ERROR_CODE_MAP = {
+ DUPLICATE_KEY: 'mds.error.DUPLICATE_KEY',
+ INVALID_FORMAT: 'mds.error.INVALID_FORMAT',
+ FOREIGN_KEY_VIOLATION: 'mds.error.FOREIGN_KEY_VIOLATION',
+ VALIDATION_FAILED: 'mds.error.VALIDATION_FAILED'
+};
+```
+
+**验收标准**:
+- [ ] 错误码定义完整
+- [ ] 映射关系正确
+- [ ] 命名规范统一
+
+**工作量**: 0.5天
+
+---
+
+#### 任务3.2: 实现错误翻译函数
+
+**任务描述**: 实现后端错误消息的统一翻译函数
+
+**涉及文件**:
+- `static/mds/js/common.js` (新增translateError函数)
+
+**具体工作**:
+- 实现translateError函数
+- 处理错误码和参数
+- 支持参数化翻译
+- 提供回退机制
+
+**关键代码**:
+```javascript
+function translateError(response) {
+ // 检查是否有错误对象
+ if (!response.error) {
+ return response.message || 'Unknown error';
+ }
+
+ const { code, params = {} } = response.error;
+ const errorKey = ERROR_CODE_MAP[code] || `mds.error.${code}`;
+
+ // 使用i18n翻译
+ if (typeof i18n !== 'undefined') {
+ const translated = i18n.t(errorKey, params);
+ // 如果翻译键不存在,返回错误码
+ if (translated !== errorKey) {
+ return translated;
+ }
+ }
+
+ // 回退:返回错误码
+ return `Error: ${code}`;
+}
+```
+
+**验收标准**:
+- [ ] 函数正确处理错误码
+- [ ] 支持参数化翻译
+- [ ] 回退机制有效
+
+**工作量**: 1天
+
+---
+
+#### 任务3.3: 改造API错误处理
+
+**任务描述**: 改造所有API调用的错误处理
+
+**涉及文件**:
+- `static/mds/js/mds-page-controller.js`
+- `static/mds/js/data-table.js`
+- `static/mds/js/global-ops-controller.js`
+
+**具体工作**:
+- 查找所有API调用
+- 改造错误处理逻辑
+- 使用translateError函数
+
+**改造示例**:
+```javascript
+// 改造前
+async function handleResponse(response) {
+ if (!response.success) {
+ showError(response.message);
+ }
+}
+
+// 改造后
+async function handleResponse(response) {
+ if (!response.success) {
+ const errorMsg = translateError(response);
+ showError(errorMsg);
+ }
+}
+```
+
+**验收标准**:
+- [ ] 所有API错误都使用translateError
+- [ ] 错误消息支持三语言
+- [ ] 功能正常
+
+**工作量**: 1天
+
+---
+
+#### 任务3.4: 提取P4级翻译键
+
+**任务描述**: 提取错误消息相关的翻译键(约85处)
+
+**涉及文件**:
+- `static/lib/i18n/zh-CN.js`
+- `static/lib/i18n/en-US.js`
+- `static/lib/i18n/de-DE.js`
+
+**具体工作**:
+- 提取所有错误消息文本
+- 编写三语言翻译
+- 支持参数化翻译
+
+**预期产出**:
+```javascript
+// zh-CN.js
+'mds.error.DUPLICATE_KEY': '数据重复: {field}={value} 已存在',
+'mds.error.INVALID_FORMAT': '格式错误: {field} 格式不正确',
+'mds.error.FOREIGN_KEY_VIOLATION': '外键约束违反: {field} 引用不存在',
+'mds.error.VALIDATION_FAILED': '校验失败: {reason}',
+'mds.error.queryFailed': '查询失败',
+'mds.error.uploadFailed': '上传失败',
+'mds.error.timeout': '请求超时',
+// ... 共约85条
+```
+
+**验收标准**:
+- [ ] 翻译键数量正确(约85处)
+- [ ] 三语言翻译准确
+- [ ] 参数化翻译正确
+
+**工作量**: 1天
+
+---
+
+#### 任务3.5: 测试错误消息翻译
+
+**任务描述**: 测试各种错误消息的翻译效果
+
+**具体工作**:
+- 模拟各种错误响应
+- 测试三语言错误提示
+- 测试参数化翻译
+
+**验收标准**:
+- [ ] 所有错误消息翻译正确
+- [ ] 参数化翻译正确
+- [ ] 回退机制有效
+
+**工作量**: 0.5天
+
+---
+
+### 4.2 表格组件改造(Day 12-15)
+
+#### 任务3.6: 改造data-table.js基础函数
+
+**任务描述**: 改造数据表格组件的基础函数
+
+**涉及文件**:
+- `static/mds/js/data-table.js`
+
+**具体工作**:
+- 改造表格列标题翻译
+- 改造空状态提示
+- 改造加载状态提示
+- 添加语言切换事件监听
+
+**验收标准**:
+- [ ] 表格列标题支持翻译
+- [ ] 空状态提示支持翻译
+- [ ] 加载状态提示支持翻译
+
+**工作量**: 1天
+
+---
+
+#### 任务3.7: 改造分页控件
+
+**任务描述**: 改造表格分页控件的翻译
+
+**涉及文件**:
+- `static/mds/js/data-table.js`
+
+**具体工作**:
+- 改造"条/页"文本
+- 改造"共X条"文本
+- 改造"上一页"、"下一页"文本
+- 改造页码显示
+
+**预期产出**:
+```javascript
+// 翻译键
+'mds.table.perPage': '条/页',
+'mds.table.total': '共 {count} 条',
+'mds.table.prevPage': '上一页',
+'mds.table.nextPage': '下一页',
+```
+
+**验收标准**:
+- [ ] 分页控件文本翻译正确
+- [ ] 三语言显示正确
+- [ ] 功能正常
+
+**工作量**: 1天
+
+---
+
+#### 任务3.8: 改造表格操作按钮
+
+**任务描述**: 改造表格中的操作按钮(编辑、删除等)
+
+**涉及文件**:
+- `static/mds/js/data-table.js`
+
+**具体工作**:
+- 改造"编辑"按钮
+- 改造"删除"按钮
+- 改造"导出"按钮
+- 使用data-i18n属性
+
+**验收标准**:
+- [ ] 操作按钮文本翻译正确
+- [ ] 三语言显示正确
+
+**工作量**: 0.5天
+
+---
+
+#### 任务3.9: 改造空状态提示
+
+**任务描述**: 改造表格无数据时的提示文本
+
+**涉及文件**:
+- `static/mds/js/data-table.js`
+
+**具体工作**:
+- 改造"暂无数据"文本
+- 改造"加载中..."文本
+- 支持动态翻译
+
+**预期产出**:
+```javascript
+// 翻译键
+'mds.table.noData': '暂无数据',
+'mds.table.loading': '加载中...',
+```
+
+**验收标准**:
+- [ ] 空状态提示翻译正确
+- [ ] 三语言显示正确
+
+**工作量**: 0.5天
+
+---
+
+#### 任务3.10: 改造全选控件
+
+**任务描述**: 改造表格全选复选框的文本
+
+**涉及文件**:
+- `static/mds/js/data-table.js`
+
+**具体工作**:
+- 改造"全选"文本
+- 支持动态翻译
+
+**验收标准**:
+- [ ] 全选文本翻译正确
+- [ ] 三语言显示正确
+
+**工作量**: 0.5天
+
+---
+
+#### 任务3.11: 提取P5级翻译键
+
+**任务描述**: 提取表格相关文本的翻译键(约150处)
+
+**涉及文件**:
+- `static/lib/i18n/zh-CN.js`
+- `static/lib/i18n/en-US.js`
+- `static/lib/i18n/de-DE.js`
+
+**具体工作**:
+- 提取所有表格文本
+- 编写三语言翻译
+- 支持参数化翻译
+
+**验收标准**:
+- [ ] 翻译键数量正确(约150处)
+- [ ] 三语言翻译准确
+
+**工作量**: 1天
+
+---
+
+#### 任务3.12: 实现表格语言切换监听
+
+**任务描述**: 实现表格在语言切换时的刷新
+
+**涉及文件**:
+- `static/mds/js/data-table.js`
+
+**具体工作**:
+- 监听languageChanged事件
+- 实现refresh方法
+- 重新渲染表格
+
+**关键代码**:
+```javascript
+class DataTable {
+ refresh() {
+ // 重新渲染列标题
+ this.renderHeaders();
+ // 重新渲染分页控件
+ this.renderPagination();
+ // 重新渲染空状态
+ if (this.data.length === 0) {
+ this.renderEmptyState();
+ }
+ }
+}
+
+// 监听语言切换
+window.addEventListener('languageChanged', () => {
+ if (window.dataTableInstance) {
+ window.dataTableInstance.refresh();
+ }
+});
+```
+
+**验收标准**:
+- [ ] 语言切换后表格刷新
+- [ ] 数据不丢失
+- [ ] 分页状态保持
+
+**工作量**: 0.5天
+
+---
+
+#### 任务3.13: 测试表格翻译
+
+**任务描述**: 测试表格组件的翻译效果
+
+**具体工作**:
+- 测试表格列标题翻译
+- 测试分页控件翻译
+- 测试空状态提示翻译
+- 测试操作按钮翻译
+
+**验收标准**:
+- [ ] 所有表格文本翻译正确
+- [ ] 语言切换后表格更新
+- [ ] 功能正常
+
+**工作量**: 0.5天
+
+---
+
+#### 任务3.14: 测试德语表格布局
+
+**任务描述**: 测试德语环境下表格的布局
+
+**具体工作**:
+- 检查列宽是否足够
+- 检查按钮是否溢出
+- 必要时调整CSS样式
+
+**验收标准**:
+- [ ] 无文本溢出
+- [ ] 无布局错乱
+- [ ] 表格美观
+
+**工作量**: 0.5天
+
+---
+
+#### 任务3.15: 编写Phase 3测试报告
+
+**任务描述**: 记录Phase 3的测试结果
+
+**涉及文件**:
+- `docs/spec/MDS-i18n-Phase3-Test-Report.md` (新建)
+
+**验收标准**:
+- [ ] 测试报告完整
+- [ ] 所有问题已解决
+
+**工作量**: 0.5天
+
+---
+
+#### 任务3.16: Phase 3总结与评审
+
+**任务描述**: 总结Phase 3工作,进行代码评审
+
+**验收标准**:
+- [ ] 代码质量合格
+- [ ] 翻译键规范统一
+- [ ] 评审通过
+
+**工作量**: 0.5天
+
+---
+
+#### 任务3.17: 提交Phase 3代码
+
+**任务描述**: 提交Phase 3的代码变更
+
+**验收标准**:
+- [ ] 代码提交成功
+- [ ] 提交信息清晰
+
+**工作量**: 0.5天
+
+---
+
+#### 任务3.18: 改造表格筛选控件
+
+**任务描述**: 改造表格筛选区域的文本翻译
+
+**涉及文件**:
+- `static/mds/js/data-table.js`
+- `static/mds/pages/template.html`
+
+**具体工作**:
+- 改造筛选按钮文本
+- 改造筛选条件显示
+- 改造重置按钮文本
+
+**验收标准**:
+- [ ] 筛选控件文本翻译正确
+- [ ] 三语言显示正确
+
+**工作量**: 0.5天
+
+---
+
+## 五、Phase 4任务清单(完成与测试,4天)
+
+### 5.1 完成剩余翻译(Day 16-17)
+
+#### 任务4.1: 改造mds-page-controller.js
+
+**任务描述**: 完成页面控制器的剩余翻译
+
+**涉及文件**:
+- `static/mds/js/mds-page-controller.js`
+
+**具体工作**:
+- 查找所有硬编码中文文本
+- 替换为i18n.t()调用
+- 确保所有文本都支持翻译
+
+**验收标准**:
+- [ ] 无硬编码中文文本
+- [ ] 所有文本支持翻译
+- [ ] 功能正常
+
+**工作量**: 1天
+
+---
+
+#### 任务4.2: 改造global-ops-controller.js
+
+**任务描述**: 完成全局操作控制器的翻译
+
+**涉及文件**:
+- `static/mds/js/global-ops-controller.js`
+
+**具体工作**:
+- 改造全局操作按钮
+- 改造操作提示消息
+- 改造确认对话框
+
+**验收标准**:
+- [ ] 所有文本支持翻译
+- [ ] 功能正常
+
+**工作量**: 0.5天
+
+---
+
+#### 任务4.3: 提取P6级翻译键
+
+**任务描述**: 提取剩余文本的翻译键(约304处)
+
+**涉及文件**:
+- `static/lib/i18n/zh-CN.js`
+- `static/lib/i18n/en-US.js`
+- `static/lib/i18n/de-DE.js`
+
+**具体工作**:
+- 提取所有剩余文本
+- 编写三语言翻译
+- 检查翻译键完整性
+
+**验收标准**:
+- [ ] 翻译键数量正确(约304处)
+- [ ] 三语言翻译准确
+- [ ] 无遗漏文本
+
+**工作量**: 1天
+
+---
+
+#### 任务4.4: 检查翻译键完整性
+
+**任务描述**: 检查所有文件中使用的翻译键是否都存在
+
+**具体工作**:
+- 扫描所有JS文件中的i18n.t()调用
+- 提取所有翻译键
+- 对比语言包中的键
+- 补充缺失的翻译键
+
+**验收标准**:
+- [ ] 无缺失的翻译键
+- [ ] 三语言包键数量一致
+- [ ] 无未使用的翻译键
+
+**工作量**: 0.5天
+
+---
+
+#### 任务4.5: 清理调试代码
+
+**任务描述**: 清理临时添加的调试代码和注释
+
+**涉及文件**:
+- 所有修改过的文件
+
+**具体工作**:
+- 删除console.log调试语句
+- 删除临时注释
+- 整理代码格式
+
+**验收标准**:
+- [ ] 无调试代码
+- [ ] 代码整洁
+- [ ] 注释清晰
+
+**工作量**: 0.5天
+
+---
+
+### 5.2 全面测试(Day 18-19)
+
+#### 任务4.6: 功能测试-所有页面
+
+**任务描述**: 测试所有页面的翻译功能
+
+**具体工作**:
+- 测试index.html所有文本翻译
+- 测试template.html所有文本翻译
+- 测试user-guide.html所有文本翻译
+- 测试所有弹窗文本翻译
+
+**验收标准**:
+- [ ] 所有页面文本正确翻译
+- [ ] 三语言显示正确
+- [ ] 功能正常
+
+**工作量**: 1天
+
+---
+
+#### 任务4.7: 功能测试-三语言切换
+
+**任务描述**: 测试三语言之间的切换功能
+
+**具体工作**:
+- 测试中文切换到英文
+- 测试英文切换到德语
+- 测试德语切换到中文
+- 测试语言选择的持久化
+
+**验收标准**:
+- [ ] 切换流畅,无白屏
+- [ ] 状态保持
+- [ ] 语言持久化到localStorage
+
+**工作量**: 0.5天
+
+---
+
+#### 任务4.8: 功能测试-无刷新切换
+
+**任务描述**: 测试无刷新语言切换的所有场景
+
+**具体工作**:
+- 测试表格筛选状态下切换
+- 测试分页状态下切换
+- 测试表单输入状态下切换
+- 测试弹窗打开状态下切换
+- 测试滚动位置保持
+
+**验收标准**:
+- [ ] 筛选条件不丢失
+- [ ] 分页状态保持
+- [ ] 表单输入保持
+- [ ] 弹窗不关闭
+- [ ] 滚动位置保持
+
+**工作量**: 0.5天
+
+---
+
+#### 任务4.9: 性能测试
+
+**任务描述**: 测试国际化功能对性能的影响
+
+**具体工作**:
+- 测试语言包加载时间
+- 测试语言切换响应时间
+- 测试内存占用
+- 对比改造前后的性能
+
+**验收标准**:
+- [ ] 语言包加载时间 < 300ms
+- [ ] 语言切换响应时间 < 100ms
+- [ ] 内存占用增加 < 8MB
+
+**工作量**: 0.5天
+
+---
+
+#### 任务4.10: 浏览器兼容性测试
+
+**任务描述**: 测试不同浏览器的兼容性
+
+**具体工作**:
+- Chrome浏览器测试
+- Firefox浏览器测试
+- Edge浏览器测试
+- Safari浏览器测试(如有条件)
+
+**验收标准**:
+- [ ] 所有浏览器正常运行
+- [ ] 德语特殊字符正确显示
+- [ ] 无布局错乱
+
+**工作量**: 0.5天
+
+---
+
+#### 任务4.11: 德语专项测试
+
+**任务描述**: 德语环境的专项测试
+
+**具体工作**:
+- 测试德语所有文本显示
+- 测试德语特殊字符
+- 测试德语长文本布局
+- 测试德语文本换行
+
+**验收标准**:
+- [ ] 德语所有文本正确显示
+- [ ] 特殊字符无乱码
+- [ ] 长文本布局合理
+
+**工作量**: 0.5天
+
+---
+
+#### 任务4.12: 回归测试
+
+**任务描述**: 确保国际化改造不影响现有功能
+
+**具体工作**:
+- 测试数据导入功能
+- 测试数据校验功能
+- 测试数据推送功能
+- 测试数据查询功能
+- 测试所有业务流程
+
+**验收标准**:
+- [ ] 所有业务功能正常
+- [ ] 无功能退化
+- [ ] 无新增bug
+
+**工作量**: 1天
+
+---
+
+#### 任务4.13: 编写最终测试报告
+
+**任务描述**: 编写完整的测试报告
+
+**涉及文件**:
+- `docs/spec/MDS-i18n-Final-Test-Report.md` (新建)
+
+**具体工作**:
+- 记录所有测试用例
+- 记录所有测试结果
+- 记录性能数据
+- 记录浏览器兼容性结果
+
+**验收标准**:
+- [ ] 测试报告完整
+- [ ] 数据准确
+- [ ] 问题已解决
+
+**工作量**: 0.5天
+
+---
+
+#### 任务4.14: 编写实施文档
+
+**任务描述**: 编写完整的实施文档
+
+**涉及文件**:
+- `docs/spec/MDS-i18n-Implementation-Summary.md` (新建)
+
+**具体工作**:
+- 记录实施过程
+- 记录技术方案
+- 记录关键决策
+- 记录维护指南
+
+**验收标准**:
+- [ ] 文档完整
+- [ ] 内容清晰
+- [ ] 可维护性强
+
+**工作量**: 0.5天
+
+---
+
+## 六、任务依赖关系图
+
+### 6.1 Phase内部依赖
+
+```
+Phase 1:
+任务1.1 → 任务1.2 → 任务1.3 (语言包创建,串行)
+任务1.4 → 任务1.5 → 任务1.6 (状态枚举改造,串行)
+任务1.7 → 任务1.8 (UI实现,串行)
+任务1.9, 1.10, 1.11, 1.12 (测试,并行,依赖1.1-1.8)
+任务1.13 → 任务1.14 (文档与评审,串行)
+
+Phase 2:
+任务2.1, 2.2, 2.3 (HTML改造,并行)
+任务2.4 (翻译键提取,依赖2.1-2.3)
+任务2.6 → 任务2.7 → 任务2.8 → 任务2.9 (弹窗改造,串行)
+任务2.10 (翻译键提取,依赖2.6-2.9)
+任务2.11 (语言切换监听,依赖2.6)
+任务2.12, 2.13 (测试,并行,依赖2.1-2.11)
+任务2.14 → 任务2.15 → 任务2.16 (文档与提交,串行)
+
+Phase 3:
+任务3.1 → 任务3.2 → 任务3.3 (错误处理,串行)
+任务3.4 (翻译键提取,依赖3.1-3.3)
+任务3.5 (测试,依赖3.1-3.4)
+任务3.6 → 任务3.7 → 任务3.8 → 任务3.9 → 任务3.10 (表格改造,串行)
+任务3.11 (翻译键提取,依赖3.6-3.10)
+任务3.12 (语言切换监听,依赖3.6)
+任务3.13, 3.14, 3.18 (测试,并行,依赖3.6-3.12)
+任务3.15 → 任务3.16 → 任务3.17 (文档与提交,串行)
+
+Phase 4:
+任务4.1, 4.2 (代码改造,并行)
+任务4.3 (翻译键提取,依赖4.1-4.2)
+任务4.4 → 任务4.5 (检查与清理,串行)
+任务4.6, 4.7, 4.8, 4.9, 4.10, 4.11, 4.12 (全面测试,并行)
+任务4.13 → 任务4.14 (文档编写,串行)
+```
+
+### 6.2 Phase间依赖
+
+```
+Phase 1完成 → Phase 2开始
+Phase 2完成 → Phase 3开始
+Phase 3完成 → Phase 4开始
+```
+
+### 6.3 关键路径
+
+```
+任务1.1 → 任务1.4 → 任务1.8 → 任务2.6 → 任务3.1 → 任务3.6 → 任务4.1 → 任务4.6 → 任务4.12
+```
+
+关键路径上的任务不可延误,否则会影响整体进度。
+
+---
+
+## 七、风险应对预案
+
+### 7.1 技术风险
+
+#### 风险1: i18n框架兼容性问题
+
+**风险描述**: Monitor模块的i18n框架可能不兼容MDS模块
+
+**应对措施**:
+- Phase 1先测试框架复用可行性
+- 如不兼容,独立实现轻量级i18n
+- 预留1天应急时间
+
+**触发条件**: Phase 1任务1.8测试失败
+
+**应急方案**: 创建独立的MDS-i18n实例
+
+---
+
+#### 风险2: 状态枚举改造复杂度超预期
+
+**风险描述**: 状态枚举在多处使用,改造难度超预期
+
+**应对措施**:
+- 先梳理所有使用场景
+- 采用渐进式改造
+- 预留0.5天应急时间
+
+**触发条件**: 任务1.4-1.6耗时超过1.5天
+
+**应急方案**: 分批改造,优先P0级
+
+---
+
+#### 风险3: 后端错误码改造复杂
+
+**风险描述**: 后端返回错误码格式不统一
+
+**应对措施**:
+- 先调研现有错误格式
+- 设计兼容多种格式的translateError
+- 与后端协调统一错误格式
+
+**触发条件**: 任务3.2发现错误格式不统一
+
+**应急方案**: 前端增加错误格式适配逻辑
+
+---
+
+### 7.2 进度风险
+
+#### 风险4: 翻译工作量超预期
+
+**风险描述**: 翻译文本数量超出预估(746处)
+
+**应对措施**:
+- 使用脚本批量提取中文文本
+- 使用翻译工具辅助翻译
+- 分优先级,P0-P3优先
+
+**触发条件**: Phase 2结束时翻译完成度<80%
+
+**应急方案**: 延长Phase 3时间,压缩Phase 4测试时间
+
+---
+
+#### 风险5: 测试发现大量问题
+
+**风险描述**: Phase 4测试发现大量翻译错误或功能bug
+
+**应对措施**:
+- 每个Phase完成后立即测试
+- 及时修复发现的问题
+- 避免问题积累到Phase 4
+
+**触发条件**: Phase 4测试发现严重问题>5个
+
+**应急方案**: 回退到上一个Phase,修复后重新测试
+
+---
+
+### 7.3 质量风险
+
+#### 风险6: 德语翻译质量差
+
+**风险描述**: 德语翻译不准确或不符合德语习惯
+
+**应对措施**:
+- 使用专业翻译工具(DeepL)
+- 德语文本专项测试
+- 必要时人工校对
+
+**触发条件**: 德语专项测试发现翻译错误>10处
+
+**应急方案**: 重新翻译德语文本,人工校对
+
+---
+
+#### 风险7: 德语文本布局问题
+
+**风险描述**: 德语文本过长导致布局错乱
+
+**应对措施**:
+- Phase 1即开始布局测试
+- 使用CSS自适应样式
+- 必要时调整组件宽度
+
+**触发条件**: 德语布局测试发现溢出>5处
+
+**应急方案**: 调整CSS样式,增加德语专项布局适配
+
+---
+
+### 7.4 协作风险
+
+#### 风险8: 与Monitor模块冲突
+
+**风险描述**: MDS的i18n改造影响Monitor模块
+
+**应对措施**:
+- 使用命名空间隔离(monitor., mds.)
+- 测试Monitor模块功能
+- 确保向后兼容
+
+**触发条件**: Monitor模块功能异常
+
+**应急方案**: 回退MDS改造,检查命名空间冲突
+
+---
+
+#### 风险9: 浏览器兼容性问题
+
+**风险描述**: 某些浏览器显示异常
+
+**应对措施**:
+- 早期进行浏览器测试
+- 使用标准API,避免浏览器特定特性
+- 提供降级方案
+
+**触发条件**: 浏览器测试发现兼容性问题
+
+**应急方案**: 针对特定浏览器提供降级方案
+
+---
+
+### 7.5 风险监控
+
+**监控频率**: 每日检查
+
+**监控指标**:
+- 当前进度vs计划进度
+- 测试通过率
+- 发现问题数量
+- 代码质量指标
+
+**预警阈值**:
+- 进度偏差 > 20%
+- 测试通过率 < 80%
+- 严重问题 > 3个
+
+**应对流程**:
+1. 发现风险 → 评估影响 → 制定应对方案 → 执行应对 → 跟踪效果
+
+---
+
+## 八、附录
+
+### 8.1 翻译键命名规范
+
+**命名规则**: `{module}.{category}.{item}`
+
+**示例**:
+- `mds.status.pending` - MDS模块的状态文本
+- `mds.action.validate` - MDS模块的操作按钮
+- `mds.error.DUPLICATE_KEY` - MDS模块的错误消息
+- `mds.modal.confirm` - MDS模块的弹窗文本
+- `mds.table.noData` - MDS模块的表格文本
+
+**分类**:
+- `status` - 状态标签
+- `action` - 操作按钮
+- `nav` - 导航菜单
+- `modal` - 弹窗文本
+- `error` - 错误消息
+- `table` - 表格文本
+- `upload` - 上传相关
+- `validation` - 校验相关
+- `sync` - 推送相关
+
+---
+
+### 8.2 相关文档
+
+- [MDS-i18n实施方案](./MDS-i18n-Implementation-Proposal.md)
+- [Monitor-i18n实施计划](./Monitor-i18n-Implementation-Plan.md)
+- [AGENTS.md开发指南](../../AGENTS.md)
+
+---
+
+### 8.3 变更历史
+
+| 版本 | 日期 | 变更内容 | 作者 |
+|------|------|----------|------|
+| v1.0 | 2026-05-24 | 初版创建,基于实施方案生成详细任务清单 | CodeArts |
+
+---
+
+**文档状态**: 待执行
+**下一步**: 开始Phase 1任务1.1 - 扩展zh-CN.js语言包
diff --git a/static/lib/i18n/de-DE.js b/static/lib/i18n/de-DE.js
index 506fbf6..750a918 100644
--- a/static/lib/i18n/de-DE.js
+++ b/static/lib/i18n/de-DE.js
@@ -4,497 +4,507 @@
*/
window.__i18n_de_DE__ = {
// ============ Seitentitel ============
- 'page.title': 'MyAPI Systemüberwachung',
- 'page.live_logs': 'Echtzeit-Logs - Systemüberwachung',
- 'page.history_logs': 'Protokollverlauf - Systemüberwachung',
+ 'monitor.page.title': 'MyAPI Systemüberwachung',
+ 'monitor.page.live_logs': 'Echtzeit-Logs - Systemüberwachung',
+ 'monitor.page.history_logs': 'Protokollverlauf - Systemüberwachung',
// ============ Navigation ============
- 'nav.overview': '📊 Übersicht',
- 'nav.database': '🗃️ Datenbank',
- 'nav.events': '☎️ Ereignisse',
- 'nav.scheduler': '⏰ Planer',
- 'nav.http_requests': '📥 HTTP-Anfragen',
- 'nav.outbound_requests': '📤 Ausgehend',
- 'nav.logs': '📋 Protokolle',
+ 'monitor.nav.overview': '📊 Übersicht',
+ 'monitor.nav.database': '🗃️ Datenbank',
+ 'monitor.nav.events': '☎️ Ereignisse',
+ 'monitor.nav.scheduler': '⏰ Planer',
+ 'monitor.nav.http_requests': '📥 HTTP-Anfragen',
+ 'monitor.nav.outbound_requests': '📤 Ausgehend',
+ 'monitor.nav.logs': '📋 Protokolle',
// ============ Tabs ============
- 'tab.overview': 'Übersicht',
- 'tab.database': 'Datenbank',
- 'tab.events': 'Ereignisse',
- 'tab.scheduler': 'Planer',
- 'tab.http': 'HTTP-Anfragen',
- 'tab.outbound': 'Ausgehend',
- 'tab.logs': 'Systemprotokolle',
- 'tab.timeline': 'Zeitachse',
- 'tab.chart': 'Diagramme',
+ 'monitor.tab.overview': 'Übersicht',
+ 'monitor.tab.database': 'Datenbank',
+ 'monitor.tab.events': 'Ereignisse',
+ 'monitor.tab.scheduler': 'Planer',
+ 'monitor.tab.http': 'HTTP-Anfragen',
+ 'monitor.tab.outbound': 'Ausgehend',
+ 'monitor.tab.logs': 'Systemprotokolle',
+ 'monitor.tab.timeline': 'Zeitachse',
+ 'monitor.tab.chart': 'Diagramme',
// ============ Karten ============
- 'card.resource': 'Ressourcennutzung',
- 'card.db_status': 'Datenbankstatus',
- 'card.db_connections': 'Datenbankverbindungen',
- 'card.event_helpers': 'Ereignis-Helfer',
- 'card.scheduler': 'Aufgabenplaner',
- 'card.api_requests': 'HTTP-Anfragen',
- 'card.outbound_requests': 'Ausgehende Anfragen',
- 'card.redis': 'Redis-Status',
- 'card.event_listener': 'Ereignis-Listener',
- 'card.recent_alerts': 'Aktuelle Warnungen',
- 'card.mysql': 'MySQL',
- 'card.http_requests_log': 'HTTP-Anfragedatensätze',
- 'card.scheduler_detail': 'Planer-Details',
- 'card.outbound_requests_log': 'Ausgehende Anfragedatensätze',
- 'card.callback_tracker': 'Callback-Tracker',
- 'card.event_deduplicator': 'Ereignis-Deduplikator',
+ 'monitor.card.resource': 'Ressourcennutzung',
+ 'monitor.card.db_status': 'Datenbankstatus',
+ 'monitor.card.db_connections': 'Datenbankverbindungen',
+ 'monitor.card.event_helpers': 'Ereignis-Helfer',
+ 'monitor.card.scheduler': 'Aufgabenplaner',
+ 'monitor.card.api_requests': 'HTTP-Anfragen',
+ 'monitor.card.outbound_requests': 'Ausgehende Anfragen',
+ 'monitor.card.redis': 'Redis-Status',
+ 'monitor.card.event_listener': 'Ereignis-Listener',
+ 'monitor.card.recent_alerts': 'Aktuelle Warnungen',
+ 'monitor.card.mysql': 'MySQL',
+ 'monitor.card.http_requests_log': 'HTTP-Anfragedatensätze',
+ 'monitor.card.scheduler_detail': 'Planer-Details',
+ 'monitor.card.outbound_requests_log': 'Ausgehende Anfragedatensätze',
+ 'monitor.card.callback_tracker': 'Callback-Tracker',
+ 'monitor.card.event_deduplicator': 'Ereignis-Deduplikator',
// ============ Kennzahlen ============
- 'metric.cpu': 'CPU-Auslastung',
- 'metric.memory': 'Speichernutzung',
- 'metric.threads': 'Threads',
- 'metric.uptime': 'Laufzeit',
- 'metric.total_connections': 'Verbindungen',
- 'metric.healthy': 'Gesund',
- 'metric.unhealthy': 'Fehlerhaft',
- 'metric.degraded': 'Eingeschränkt',
- 'metric.total': 'Gesamt',
- 'metric.success': 'Erfolg',
- 'metric.failed': 'Fehler',
- 'metric.pending': 'Ausstehend',
- 'metric.active': 'Aktiv',
- 'metric.idle': 'Inaktiv',
- 'metric.unknown': 'Unbekannt',
- 'metric.warning': 'Warnung',
- 'metric.events_received': 'Empfangene Ereignisse',
- 'metric.events_processed': 'Verarbeitete Ereignisse',
- 'metric.events_failed': 'Fehlgeschlagene Ereignisse',
- 'metric.jobs_running': 'Laufende Aufgaben',
- 'metric.jobs_pending': 'Ausstehende Aufgaben',
- 'metric.requests_total': 'Anfragen Gesamt',
- 'metric.requests_slow': 'Langsame Anfragen',
- 'metric.requests_error': 'Fehlerhafte Anfragen',
- 'metric.error_rate': 'Fehlerquote',
- 'metric.avg_time': 'Durchschn. Antwort',
- 'metric.connection_status': 'Verbindungsstatus',
- 'metric.host': 'Host',
- 'metric.port': 'Port',
- 'metric.database': 'Datenbank',
- 'metric.used_connections': 'Verwendete Verbindungen',
- 'metric.max_connections': 'Max. Verbindungen',
- 'metric.connection_usage': 'Verbindungsnutzung',
- 'metric.buffer_size': 'Puffergröße',
- 'metric.buffer_threshold': 'Pufferschwellwert',
- 'metric.buffer_usage': 'Puffernutzung',
- 'metric.events_interrupted': 'Unterbrochen',
- 'metric.overall_success_rate': 'Gesamterfolgsquote',
- 'metric.active_event_types': 'Aktive Ereignistypen',
- 'metric.backpressure_status': 'Rückstau-Status',
- 'metric.backpressure_pending': 'Ausstehende Ereignisse',
- 'metric.backpressure_usage': 'Rückstau-Nutzung',
- 'metric.event_loop_status': 'Ereignisschleifen-Status',
- 'metric.pending_callbacks': 'Ausstehende Callbacks',
- 'metric.max_retries': 'Max. Wiederholungen',
- 'metric.pending_retries': 'Ausstehende Wiederholungen',
- 'metric.total_entries': 'Gesamteinträge',
- 'metric.active_items': 'Aktive Elemente',
- 'metric.ttl_seconds': 'TTL (Sekunden)',
- 'metric.max_entries': 'Max. Einträge',
- 'metric.dl_total': 'DL-Gesamt',
- 'metric.recent_dl': 'Aktuelle DL',
- 'metric.process_success_rate': 'Verarbeitungserfolgsquote',
- 'metric.scheduler_status': 'Planer-Status',
- 'metric.job_count': 'Aufgabenanzahl',
- 'metric.pending_events': 'Ausstehende Ereignisse',
- 'metric.total_events': 'Gesamtereignisse',
- 'metric.file_size': 'Dateigröße',
- 'metric.running_status': 'Betriebsstatus',
+ 'monitor.metric.cpu': 'CPU-Auslastung',
+ 'monitor.metric.memory': 'Speichernutzung',
+ 'monitor.metric.threads': 'Threads',
+ 'monitor.metric.uptime': 'Laufzeit',
+ 'monitor.metric.total_connections': 'Verbindungen',
+ 'monitor.metric.healthy': 'Gesund',
+ 'monitor.metric.unhealthy': 'Fehlerhaft',
+ 'monitor.metric.degraded': 'Eingeschränkt',
+ 'monitor.metric.total': 'Gesamt',
+ 'monitor.metric.success': 'Erfolg',
+ 'monitor.metric.failed': 'Fehler',
+ 'monitor.metric.pending': 'Ausstehend',
+ 'monitor.metric.active': 'Aktiv',
+ 'monitor.metric.idle': 'Inaktiv',
+ 'monitor.metric.unknown': 'Unbekannt',
+ 'monitor.metric.warning': 'Warnung',
+ 'monitor.metric.events_received': 'Empfangene Ereignisse',
+ 'monitor.metric.events_processed': 'Verarbeitete Ereignisse',
+ 'monitor.metric.events_failed': 'Fehlgeschlagene Ereignisse',
+ 'monitor.metric.jobs_running': 'Laufende Aufgaben',
+ 'monitor.metric.jobs_pending': 'Ausstehende Aufgaben',
+ 'monitor.metric.requests_total': 'Anfragen Gesamt',
+ 'monitor.metric.requests_slow': 'Langsame Anfragen',
+ 'monitor.metric.requests_error': 'Fehlerhafte Anfragen',
+ 'monitor.metric.error_rate': 'Fehlerquote',
+ 'monitor.metric.avg_time': 'Durchschn. Antwort',
+ 'monitor.metric.connection_status': 'Verbindungsstatus',
+ 'monitor.metric.host': 'Host',
+ 'monitor.metric.port': 'Port',
+ 'monitor.metric.database': 'Datenbank',
+ 'monitor.metric.used_connections': 'Verwendete Verbindungen',
+ 'monitor.metric.max_connections': 'Max. Verbindungen',
+ 'monitor.metric.connection_usage': 'Verbindungsnutzung',
+ 'monitor.metric.buffer_size': 'Puffergröße',
+ 'monitor.metric.buffer_threshold': 'Pufferschwellwert',
+ 'monitor.metric.buffer_usage': 'Puffernutzung',
+ 'monitor.metric.events_interrupted': 'Unterbrochen',
+ 'monitor.metric.overall_success_rate': 'Gesamterfolgsquote',
+ 'monitor.metric.active_event_types': 'Aktive Ereignistypen',
+ 'monitor.metric.backpressure_status': 'Rückstau-Status',
+ 'monitor.metric.backpressure_pending': 'Ausstehende Ereignisse',
+ 'monitor.metric.backpressure_usage': 'Rückstau-Nutzung',
+ 'monitor.metric.event_loop_status': 'Ereignisschleifen-Status',
+ 'monitor.metric.pending_callbacks': 'Ausstehende Callbacks',
+ 'monitor.metric.max_retries': 'Max. Wiederholungen',
+ 'monitor.metric.pending_retries': 'Ausstehende Wiederholungen',
+ 'monitor.metric.total_entries': 'Gesamteinträge',
+ 'monitor.metric.active_items': 'Aktive Elemente',
+ 'monitor.metric.ttl_seconds': 'TTL (Sekunden)',
+ 'monitor.metric.max_entries': 'Max. Einträge',
+ 'monitor.metric.dl_total': 'DL-Gesamt',
+ 'monitor.metric.recent_dl': 'Aktuelle DL',
+ 'monitor.metric.process_success_rate': 'Verarbeitungserfolgsquote',
+ 'monitor.metric.scheduler_status': 'Planer-Status',
+ 'monitor.metric.job_count': 'Aufgabenanzahl',
+ 'monitor.metric.pending_events': 'Ausstehende Ereignisse',
+ 'monitor.metric.total_events': 'Gesamtereignisse',
+ 'monitor.metric.file_size': 'Dateigröße',
+ 'monitor.metric.running_status': 'Betriebsstatus',
// ============ Status ============
- 'status.healthy': '● System gesund',
- 'status.unhealthy': '● System fehlerhaft',
- 'status.degraded': '● System eingeschränkt',
- 'status.running': 'Läuft',
- 'status.checking': 'Prüfe',
- 'status.stopped': 'Gestoppt',
- 'status.loading': 'Laden...',
- 'status.querying': 'Abfrage...',
- 'status.exporting': 'Exportiere...',
- 'status.no_data': 'Keine Daten',
- 'status.connected': 'Verbunden',
- 'status.connecting': 'Verbinde...',
- 'status.disconnected': 'Getrennt',
- 'status.reconnecting': 'Verbinde neu...',
- 'status.paused': 'Pausiert',
- 'status.enabled': 'Aktiviert',
- 'status.disabled': 'Deaktiviert',
- 'status.no_db_connections': 'Keine Datenbankverbindungen',
- 'status.no_scheduler': 'Keine geplanten Aufgaben',
- 'status.no_http_requests': 'Keine HTTP-Anfragen',
- 'status.no_alerts': 'Keine Warnungen',
- 'status.no_logs': 'Keine Protokolle',
- 'status.no_events': 'Keine Ereignisstatistiken',
- 'status.no_api_requests': 'Keine API-Anfragedatensätze',
- 'status.no_outbound_requests': 'Keine ausgehenden Anfragedatensätze',
- 'status.no_dead_letters': 'Keine DL',
- 'status.partial_warnings': '● Teilweise Warnungen',
- 'status.monitoring': 'Überwachung',
+ 'monitor.status.healthy': '● System gesund',
+ 'monitor.status.unhealthy': '● System fehlerhaft',
+ 'monitor.status.degraded': '● System eingeschränkt',
+ 'monitor.status.running': 'Läuft',
+ 'monitor.status.checking': 'Prüfe',
+ 'monitor.status.stopped': 'Gestoppt',
+ 'monitor.status.loading': 'Laden...',
+ 'monitor.status.querying': 'Abfrage...',
+ 'monitor.status.exporting': 'Exportiere...',
+ 'monitor.status.no_data': 'Keine Daten',
+ 'monitor.status.connected': 'Verbunden',
+ 'monitor.status.connecting': 'Verbinde...',
+ 'monitor.status.disconnected': 'Getrennt',
+ 'monitor.status.reconnecting': 'Verbinde neu...',
+ 'monitor.status.paused': 'Pausiert',
+ 'monitor.status.enabled': 'Aktiviert',
+ 'monitor.status.disabled': 'Deaktiviert',
+ 'monitor.status.no_db_connections': 'Keine Datenbankverbindungen',
+ 'monitor.status.no_scheduler': 'Keine geplanten Aufgaben',
+ 'monitor.status.no_http_requests': 'Keine HTTP-Anfragen',
+ 'monitor.status.no_alerts': 'Keine Warnungen',
+ 'monitor.status.no_logs': 'Keine Protokolle',
+ 'monitor.status.no_events': 'Keine Ereignisstatistiken',
+ 'monitor.status.no_api_requests': 'Keine API-Anfragedatensätze',
+ 'monitor.status.no_outbound_requests': 'Keine ausgehenden Anfragedatensätze',
+ 'monitor.status.no_dead_letters': 'Keine DL',
+ 'monitor.status.partial_warnings': '● Teilweise Warnungen',
+ 'monitor.status.monitoring': 'Überwachung',
// ============ Schaltflächen ============
- 'btn.query': 'Abfrage',
- 'btn.reset': 'Zurücksetzen',
- 'btn.refresh': 'Aktualisieren',
- 'btn.export': 'Exportieren',
- 'btn.export_csv': 'CSV Export',
- 'btn.export_json': 'JSON Export',
- 'btn.detail': 'Details',
- 'btn.pause': 'Pause',
- 'btn.resume': 'Fortsetzen',
- 'btn.clear': 'Löschen',
- 'btn.save': 'Speichern',
- 'btn.delete': 'Löschen',
- 'btn.close': 'Schließen',
- 'btn.confirm': 'Bestätigen',
- 'btn.cancel': 'Abbrechen',
- 'btn.test': 'Testen',
- 'btn.apply': 'Anwenden',
- 'btn.filter': 'Filtern',
- 'btn.clear_all': 'Alle löschen',
- 'btn.realtime_on': 'Echtzeit: AN',
- 'btn.realtime_off': 'Echtzeit: AUS',
- 'btn.precise_locate': '± 60s',
- 'btn.precise_cancel': '± 60s - Klick zum Abbrechen',
- 'btn.reset_stats': 'Statistik zurücksetzen',
- 'btn.flush_all': 'Alle leeren',
- 'btn.new_window': 'Neues Fenster',
- 'btn.auto_scroll': 'Auto-Scroll',
- 'btn.live_logs': 'Echtzeit-Protokolle',
- 'btn.history_query': 'Verlaufsanfrage',
- 'btn.mark_all_read': 'Alle als gelesen markieren',
- 'btn.clear_read_status': 'Lesestatus löschen',
+ 'monitor.btn.query': 'Abfrage',
+ 'monitor.btn.reset': 'Zurücksetzen',
+ 'monitor.btn.refresh': 'Aktualisieren',
+ 'monitor.btn.export': 'Exportieren',
+ 'monitor.btn.export_csv': 'CSV Export',
+ 'monitor.btn.export_json': 'JSON Export',
+ 'monitor.btn.detail': 'Details',
+ 'monitor.btn.pause': 'Pause',
+ 'monitor.btn.resume': 'Fortsetzen',
+ 'monitor.btn.clear': 'Löschen',
+ 'monitor.btn.save': 'Speichern',
+ 'monitor.btn.delete': 'Löschen',
+ 'monitor.btn.close': 'Schließen',
+ 'monitor.btn.confirm': 'Bestätigen',
+ 'monitor.btn.cancel': 'Abbrechen',
+ 'monitor.btn.test': 'Testen',
+ 'monitor.btn.apply': 'Anwenden',
+ 'monitor.btn.filter': 'Filtern',
+ 'monitor.btn.clear_all': 'Alle löschen',
+ 'monitor.btn.realtime_on': 'Echtzeit: AN',
+ 'monitor.btn.realtime_off': 'Echtzeit: AUS',
+ 'monitor.btn.precise_locate': '± 60s',
+ 'monitor.btn.precise_cancel': '± 60s - Klick zum Abbrechen',
+ 'monitor.btn.reset_stats': 'Statistik zurücksetzen',
+ 'monitor.btn.flush_all': 'Alle leeren',
+ 'monitor.btn.new_window': 'Neues Fenster',
+ 'monitor.btn.auto_scroll': 'Auto-Scroll',
+ 'monitor.btn.live_logs': 'Echtzeit-Protokolle',
+ 'monitor.btn.history_query': 'Verlaufsanfrage',
+ 'monitor.btn.mark_all_read': 'Alle als gelesen markieren',
+ 'monitor.btn.clear_read_status': 'Lesestatus löschen',
// ============ Tabellenspalten ============
- 'col.index': '#',
- 'col.time': 'Zeit',
- 'col.level': 'Stufe',
- 'col.module': 'Modul',
- 'col.message': 'Nachricht',
- 'col.method': 'Methode',
- 'col.path': 'Pfad',
- 'col.url': 'URL',
- 'col.status': 'Status',
- 'col.duration': 'Dauer',
- 'col.client_ip': 'Client-IP',
- 'col.source': 'Quelle',
- 'col.function': 'Funktion',
- 'col.line': 'Zeile',
- 'col.operation': 'Aktion',
- 'col.count': 'Anzahl',
- 'col.avg_time': 'Durchschn. Zeit',
- 'col.max_time': 'Max. Zeit',
- 'col.db_name': 'DB-Name',
- 'col.last_check': 'Letzte Prüfung',
- 'col.current_connections': 'Aktuell',
- 'col.max_connections': 'Max',
- 'col.min_connections': 'Min',
- 'col.idle_connections': 'Inaktiv',
- 'col.used_connections': 'Verwendet',
- 'col.usage': 'Nutzung',
- 'col.processed_records': 'Verarbeitet',
- 'col.timestamp': 'Zeitstempel',
- 'col.query_params': 'Abfrageparameter',
- 'col.error_message': 'Fehlermeldung',
- 'col.url': 'URL',
- 'col.is_read': 'Gelesen',
- 'col.description': 'Beschreibung',
- 'col.total_received': 'Gesamt empfangen',
- 'col.pending': 'Ausstehend',
- 'col.processed': 'Verarbeitet',
- 'col.interrupted': 'Unterbrochen',
- 'col.completion_rate': 'Abschlussrate',
- 'col.avg_latency': 'Durchschn. Latenz',
- 'col.last_action': 'Letzte Aktion',
- 'col.id': 'ID',
- 'col.event_type': 'Ereignistyp',
- 'col.table': 'Tabelle',
+ 'monitor.col.index': '#',
+ 'monitor.col.time': 'Zeit',
+ 'monitor.col.level': 'Stufe',
+ 'monitor.col.module': 'Modul',
+ 'monitor.col.message': 'Nachricht',
+ 'monitor.col.method': 'Methode',
+ 'monitor.col.path': 'Pfad',
+ 'monitor.col.url': 'URL',
+ 'monitor.col.status': 'Status',
+ 'monitor.col.duration': 'Dauer',
+ 'monitor.col.client_ip': 'Client-IP',
+ 'monitor.col.source': 'Quelle',
+ 'monitor.col.function': 'Funktion',
+ 'monitor.col.line': 'Zeile',
+ 'monitor.col.operation': 'Aktion',
+ 'monitor.col.count': 'Anzahl',
+ 'monitor.col.avg_time': 'Durchschn. Zeit',
+ 'monitor.col.max_time': 'Max. Zeit',
+ 'monitor.col.db_name': 'DB-Name',
+ 'monitor.col.last_check': 'Letzte Prüfung',
+ 'monitor.col.current_connections': 'Aktuell',
+ 'monitor.col.max_connections': 'Max',
+ 'monitor.col.min_connections': 'Min',
+ 'monitor.col.idle_connections': 'Inaktiv',
+ 'monitor.col.used_connections': 'Verwendet',
+ 'monitor.col.usage': 'Nutzung',
+ 'monitor.col.processed_records': 'Verarbeitet',
+ 'monitor.col.timestamp': 'Zeitstempel',
+ 'monitor.col.query_params': 'Abfrageparameter',
+ 'monitor.col.error_message': 'Fehlermeldung',
+ 'monitor.col.url': 'URL',
+ 'monitor.col.is_read': 'Gelesen',
+ 'monitor.col.description': 'Beschreibung',
+ 'monitor.col.total_received': 'Gesamt empfangen',
+ 'monitor.col.pending': 'Ausstehend',
+ 'monitor.col.processed': 'Verarbeitet',
+ 'monitor.col.interrupted': 'Unterbrochen',
+ 'monitor.col.completion_rate': 'Abschlussrate',
+ 'monitor.col.avg_latency': 'Durchschn. Latenz',
+ 'monitor.col.last_action': 'Letzte Aktion',
+ 'monitor.col.id': 'ID',
+ 'monitor.col.event_type': 'Ereignistyp',
+ 'monitor.col.table': 'Tabelle',
// ============ Schnellzeit ============
- 'time.start_time': 'Startzeit',
- 'time.end_time': 'Endzeit',
- 'time.last_10m': 'Letzte 10 Min',
- 'time.last_30m': 'Letzte 30 Min',
- 'time.last_1h': 'Letzte Stunde',
- 'time.last_6h': 'Letzte 6 Stunden',
- 'time.last_24h': 'Letzte 24 Stunden',
- 'time.range': 'Zeitbereich',
+ 'monitor.time.start_time': 'Startzeit',
+ 'monitor.time.end_time': 'Endzeit',
+ 'monitor.time.last_10m': 'Letzte 10 Min',
+ 'monitor.time.last_30m': 'Letzte 30 Min',
+ 'monitor.time.last_1h': 'Letzte Stunde',
+ 'monitor.time.last_6h': 'Letzte 6 Stunden',
+ 'monitor.time.last_24h': 'Letzte 24 Stunden',
+ 'monitor.time.range': 'Zeitbereich',
// ============ Filter ============
- 'filter.level': 'Alle Stufen',
- 'filter.type': 'Alle Typen',
- 'filter.module': 'Modul',
- 'filter.keyword': 'Schlüsselwort',
- 'filter.method': 'Methode',
- 'filter.client_ip': 'Client-IP',
- 'filter.status_range': 'Statusbereich',
- 'filter.duration_range': 'Dauerbereich',
- 'filter.advanced': 'Erweiterte Filter',
- 'filter.collapse': 'Einklappen',
- 'filter.expand': 'Ausklappen',
- 'filter.clear': 'Löschen',
- 'filter.active': 'Aktive Filter',
- 'filter.module_placeholder': 'Modulname eingeben',
- 'filter.keyword_placeholder': 'Schlüsselwort eingeben',
- 'filter.ip_placeholder': 'Client-IP eingeben',
- 'filter.status_placeholder': 'z.B.: 400-499',
- 'filter.duration_placeholder': 'z.B.: >1000',
- 'filter.all_logs': 'Alle Protokolle',
+ 'monitor.filter.level': 'Alle Stufen',
+ 'monitor.filter.type': 'Alle Typen',
+ 'monitor.filter.module': 'Modul',
+ 'monitor.filter.keyword': 'Schlüsselwort',
+ 'monitor.filter.method': 'Methode',
+ 'monitor.filter.client_ip': 'Client-IP',
+ 'monitor.filter.status_range': 'Statusbereich',
+ 'monitor.filter.duration_range': 'Dauerbereich',
+ 'monitor.filter.advanced': 'Erweiterte Filter',
+ 'monitor.filter.collapse': 'Einklappen',
+ 'monitor.filter.expand': 'Ausklappen',
+ 'monitor.filter.clear': 'Löschen',
+ 'monitor.filter.active': 'Aktive Filter',
+ 'monitor.filter.module_placeholder': 'Modulname eingeben',
+ 'monitor.filter.keyword_placeholder': 'Schlüsselwort eingeben',
+ 'monitor.filter.ip_placeholder': 'Client-IP eingeben',
+ 'monitor.filter.status_placeholder': 'z.B.: 400-499',
+ 'monitor.filter.duration_placeholder': 'z.B.: >1000',
+ 'monitor.filter.all_logs': 'Alle Protokolle',
// ============ Diagramme ============
- 'chart.request_trend': '📊 Anfragentrend',
- 'chart.level_distribution': '📊 Protokollstufen-Verteilung',
- 'chart.status_distribution': '📈 Statuscode-Verteilung',
- 'chart.slow_requests': '⏱️ Langsame Anfragen TOP10',
- 'chart.total_requests': 'Anfragen Gesamt',
- 'chart.error_count': 'Fehler',
- 'chart.slow_count': 'Langsame Anfragen',
- 'chart.log_count': 'Protokolle Gesamt',
- 'chart.no_slow': '✅ Keine langsamen Anfragen',
- 'chart.query_required': 'Bitte zuerst Abfrage ausführen',
- 'chart.load_failed': '❌ Diagramm-Daten laden fehlgeschlagen, bitte erneut versuchen',
- 'chart.redis_connections': 'Redis Verbindungspool-Nutzung',
- 'chart.redis_buffer': 'Redis Puffergrößen-Änderungen',
- 'chart.cpu': 'CPU',
- 'chart.memory': 'Speicher',
- 'chart.upload': 'Upload',
- 'chart.download': 'Download',
- 'chart.cpu_memory_axis': 'CPU / Speicher (%)',
- 'chart.network_axis': 'Netzwerk-Upload / Download (KB/s)',
- 'chart.used_connections': 'Verwendete Verbindungen',
- 'chart.buffer_size_mb': 'Puffergröße (MB)',
+ 'monitor.chart.request_trend': '📊 Anfragentrend',
+ 'monitor.chart.level_distribution': '📊 Protokollstufen-Verteilung',
+ 'monitor.chart.status_distribution': '📈 Statuscode-Verteilung',
+ 'monitor.chart.slow_requests': '⏱️ Langsame Anfragen TOP10',
+ 'monitor.chart.total_requests': 'Anfragen Gesamt',
+ 'monitor.chart.error_count': 'Fehler',
+ 'monitor.chart.slow_count': 'Langsame Anfragen',
+ 'monitor.chart.log_count': 'Protokolle Gesamt',
+ 'monitor.chart.no_slow': '✅ Keine langsamen Anfragen',
+ 'monitor.chart.query_required': 'Bitte zuerst Abfrage ausführen',
+ 'monitor.chart.load_failed': '❌ Diagramm-Daten laden fehlgeschlagen, bitte erneut versuchen',
+ 'monitor.chart.no_data': 'Keine Daten',
+ 'monitor.chart.no_trend': 'Keine Trenddaten',
+ 'monitor.chart.redis_connections': 'Redis Verbindungspool-Nutzung',
+ 'monitor.chart.redis_buffer': 'Redis Puffergrößen-Änderungen',
+ 'monitor.chart.cpu': 'CPU',
+ 'monitor.chart.memory': 'Speicher',
+ 'monitor.chart.upload': 'Upload',
+ 'monitor.chart.download': 'Download',
+ 'monitor.chart.cpu_memory_axis': 'CPU / Speicher (%)',
+ 'monitor.chart.network_axis': 'Netzwerk-Upload / Download (KB/s)',
+ 'monitor.chart.used_connections': 'Verwendete Verbindungen',
+ 'monitor.chart.buffer_size_mb': 'Puffergröße (MB)',
// ============ Fehler ============
- 'error.time_range_invalid': 'Startzeit kann nicht größer als Endzeit sein',
- 'error.time_range_required': 'Bitte Startzeit und Endzeit auswählen',
- 'error.query_failed': 'Abfrage fehlgeschlagen, bitte erneut versuchen',
- 'error.export_failed': 'Export fehlgeschlagen, bitte erneut versuchen',
- 'error.connection_failed': 'Verbindung fehlgeschlagen, bitte Seite aktualisieren',
- 'error.max_templates': 'Maximal 10 Vorlagen erlaubt, bitte zuerst einige löschen',
- 'error.auto_pause': 'Echtzeit-Verfolgung automatisch pausiert (über 10 Minuten)',
- 'error.load_failed': 'Laden fehlgeschlagen, bitte erneut versuchen',
- 'error.invalid_params': 'Ungültige Parameter',
- 'error.clear_dl_failed': 'DeadLetter-Warteschlange löschen fehlgeschlagen',
+ 'monitor.error.time_range_invalid': 'Startzeit kann nicht größer als Endzeit sein',
+ 'monitor.error.time_range_required': 'Bitte Startzeit und Endzeit auswählen',
+ 'monitor.error.query_failed': 'Abfrage fehlgeschlagen, bitte erneut versuchen',
+ 'monitor.error.export_failed': 'Export fehlgeschlagen, bitte erneut versuchen',
+ 'monitor.error.connection_failed': 'Verbindung fehlgeschlagen, bitte Seite aktualisieren',
+ 'monitor.error.max_templates': 'Maximal 10 Vorlagen erlaubt, bitte zuerst einige löschen',
+ 'monitor.error.auto_pause': 'Echtzeit-Verfolgung automatisch pausiert (über 10 Minuten)',
+ 'monitor.error.load_failed': 'Laden fehlgeschlagen, bitte erneut versuchen',
+ 'monitor.error.invalid_params': 'Ungültige Parameter',
+ 'monitor.error.clear_dl_failed': 'DeadLetter-Warteschlange löschen fehlgeschlagen',
// ============ Erfolg ============
- 'success.query_complete': 'Abfrage abgeschlossen',
- 'success.export_complete': 'Export abgeschlossen',
- 'success.template_saved': 'Vorlage "{name}" gespeichert',
- 'success.logs_cleared': 'Protokolle gelöscht',
- 'success.operation_success': 'Operation erfolgreich',
- 'success.dl_cleared': 'DeadLetter-Warteschlange gelöscht',
+ 'monitor.success.query_complete': 'Abfrage abgeschlossen',
+ 'monitor.success.export_complete': 'Export abgeschlossen',
+ 'monitor.success.template_saved': 'Vorlage "{name}" gespeichert',
+ 'monitor.success.logs_cleared': 'Protokolle gelöscht',
+ 'monitor.success.operation_success': 'Operation erfolgreich',
+ 'monitor.success.dl_cleared': 'DeadLetter-Warteschlange gelöscht',
// ============ Zeitachse ============
- 'timeline.title': 'Zeitachse',
- 'timeline.no_data': 'Keine Daten, bitte zuerst Abfrage ausführen',
- 'timeline.anomaly_detected': '⚠️ {count} Anomalien erkannt',
- 'timeline.error_burst': '{count} aufeinanderfolgende FEHLER-Protokolle',
- 'timeline.slow_anomaly': 'Langsame Anfrage Anomalie ({duration}ms > durchschn. {avg}ms×3)',
- 'timeline.duplicate_error': 'Duplizierter Fehler "{msg}" {count} mal aufgetreten',
- 'timeline.http_request': 'HTTP-Anfrage',
- 'timeline.outbound_request': 'Ausgehende Anfrage',
- 'timeline.system_log': 'Systemprotokoll',
+ 'monitor.timeline.title': 'Zeitachse',
+ 'monitor.timeline.no_data': 'Keine Daten, bitte zuerst Abfrage ausführen',
+ 'monitor.timeline.anomaly_detected': '⚠️ {count} Anomalien erkannt',
+ 'monitor.timeline.error_burst': '{count} aufeinanderfolgende FEHLER-Protokolle',
+ 'monitor.timeline.slow_anomaly': 'Langsame Anfrage Anomalie ({duration}ms > durchschn. {avg}ms×3)',
+ 'monitor.timeline.duplicate_error': 'Duplizierter Fehler "{msg}" {count} mal aufgetreten',
+ 'monitor.timeline.http_request': 'HTTP-Anfrage',
+ 'monitor.timeline.outbound_request': 'Ausgehende Anfrage',
+ 'monitor.timeline.system_log': 'Systemprotokoll',
+ 'monitor.timeline.summary_title': '📊 Timeline-Statistiken',
+ 'monitor.timeline.total_events': 'Gesamtereignisse',
+ 'monitor.timeline.http_requests': 'HTTP-Anfragen',
+ 'monitor.timeline.outbound_requests': 'Ausgehende Anfragen',
+ 'monitor.timeline.system_logs': 'Systemprotokolle',
+ 'monitor.timeline.errors': 'Fehler',
+ 'monitor.timeline.warnings': 'Warnungen',
+ 'monitor.timeline.slow_requests': 'Langsame Anfragen',
// ============ Paginierung ============
- 'pagination.page': 'Seite',
- 'pagination.of': 'von',
- 'pagination.items': 'Einträge',
- 'pagination.showing': 'Anzeigen',
- 'pagination.per_page': 'Pro Seite',
- 'pagination.first': 'Erste',
- 'pagination.last': 'Letzte',
- 'pagination.prev': 'Vorherige',
- 'pagination.next': 'Nächste',
- 'pagination.go_to': 'Gehe zu',
+ 'monitor.pagination.page': 'Seite',
+ 'monitor.pagination.of': 'von',
+ 'monitor.pagination.items': 'Einträge',
+ 'monitor.pagination.showing': 'Anzeigen',
+ 'monitor.pagination.per_page': 'Pro Seite',
+ 'monitor.pagination.first': 'Erste',
+ 'monitor.pagination.last': 'Letzte',
+ 'monitor.pagination.prev': 'Vorherige',
+ 'monitor.pagination.next': 'Nächste',
+ 'monitor.pagination.go_to': 'Gehe zu',
// ============ Export ============
- 'export.current_page': 'Aktuelle Seite exportieren',
- 'export.all_data': 'Alle Daten exportieren',
- 'export.format_csv': 'CSV-Format',
- 'export.format_json': 'JSON-Format',
- 'export.select_format': 'Format auswählen',
+ 'monitor.export.current_page': 'Aktuelle Seite exportieren',
+ 'monitor.export.all_data': 'Alle Daten exportieren',
+ 'monitor.export.format_csv': 'CSV-Format',
+ 'monitor.export.format_json': 'JSON-Format',
+ 'monitor.export.select_format': 'Format auswählen',
// ============ Vorlagen ============
- 'template.saved_queries': 'Gespeicherte Abfragen...',
- 'template.save': 'Abfrage speichern',
- 'template.manage': 'Vorlagen verwalten',
- 'template.name': 'Vorlagenname',
- 'template.description': 'Beschreibung',
- 'template.delete_confirm': 'Diese Vorlage löschen?',
- 'template.empty': 'Keine gespeicherten Vorlagen',
+ 'monitor.template.saved_queries': 'Gespeicherte Abfragen...',
+ 'monitor.template.save': 'Abfrage speichern',
+ 'monitor.template.manage': 'Vorlagen verwalten',
+ 'monitor.template.name': 'Vorlagenname',
+ 'monitor.template.description': 'Beschreibung',
+ 'monitor.template.delete_confirm': 'Diese Vorlage löschen?',
+ 'monitor.template.empty': 'Keine gespeicherten Vorlagen',
// ============ Statistiken ============
- 'stats.time_range': 'Abfragezeitbereich',
- 'stats.http_requests': 'HTTP-Anfragen',
- 'stats.outbound_requests': 'Ausgehende Anfragen',
- 'stats.system_logs': 'Systemprotokolle',
- 'stats.level_distribution': 'Stufenverteilung',
- 'stats.all_time': 'Gesamte Zeit',
+ 'monitor.stats.time_range': 'Abfragezeitbereich',
+ 'monitor.stats.http_requests': 'HTTP-Anfragen',
+ 'monitor.stats.outbound_requests': 'Ausgehende Anfragen',
+ 'monitor.stats.system_logs': 'Systemprotokolle',
+ 'monitor.stats.level_distribution': 'Stufenverteilung',
+ 'monitor.stats.all_time': 'Gesamte Zeit',
// ============ Sonstiges ============
- 'other.last_update': 'Letzte Aktualisierung',
- 'other.auto_reconnect': 'Neuverbindung in 5s',
- 'other.waiting_logs': 'Warte auf Protokolldaten...',
- 'other.no_matching_logs': 'Keine passenden Protokolle',
- 'other.precise_mode': 'Präziser Modus',
- 'other.saved_queries': 'Gespeicherte Abfragen...',
- 'other.all_time': 'Gesamte Zeit',
- 'other.linked_query': 'Verknüpfte Abfrage',
- 'other.seconds': 'Sekunden',
- 'other.minutes': 'Minuten',
- 'other.hours': 'Stunden',
- 'other.days': 'Tage',
- 'other.ms': 'ms',
- 'other.times': 'mal',
- 'other.view_detail': 'Details anzeigen',
- 'other.copy': 'Kopieren',
- 'other.expand': 'Ausklappen',
- 'other.collapse': 'Einklappen',
- 'other.show_internal': 'Intern anzeigen',
- 'other.show_read': 'Gelesen anzeigen',
+ 'monitor.other.last_update': 'Letzte Aktualisierung',
+ 'monitor.other.auto_reconnect': 'Neuverbindung in 5s',
+ 'monitor.other.waiting_logs': 'Warte auf Protokolldaten...',
+ 'monitor.other.no_matching_logs': 'Keine passenden Protokolle',
+ 'monitor.other.precise_mode': 'Präziser Modus',
+ 'monitor.other.saved_queries': 'Gespeicherte Abfragen...',
+ 'monitor.other.all_time': 'Gesamte Zeit',
+ 'monitor.other.linked_query': 'Verknüpfte Abfrage',
+ 'monitor.other.seconds': 'Sekunden',
+ 'monitor.other.minutes': 'Minuten',
+ 'monitor.other.hours': 'Stunden',
+ 'monitor.other.days': 'Tage',
+ 'monitor.other.ms': 'ms',
+ 'monitor.other.times': 'mal',
+ 'monitor.other.view_detail': 'Details anzeigen',
+ 'monitor.other.copy': 'Kopieren',
+ 'monitor.other.expand': 'Ausklappen',
+ 'monitor.other.collapse': 'Einklappen',
+ 'monitor.other.show_internal': 'Intern anzeigen',
+ 'monitor.other.show_read': 'Gelesen anzeigen',
// ============ Datum ============
- 'date.today': 'Heute',
- 'date.yesterday': 'Gestern',
+ 'monitor.date.today': 'Heute',
+ 'monitor.date.yesterday': 'Gestern',
// ============ Sprache ============
- 'lang.select': 'Sprache auswählen',
- 'lang.zh': '中文',
- 'lang.en': 'English',
- 'lang.de': 'Deutsch',
+ 'monitor.lang.select': 'Sprache auswählen',
+ 'monitor.lang.zh': '中文',
+ 'monitor.lang.en': 'English',
+ 'monitor.lang.de': 'Deutsch',
// ============ HTTP-Methoden ============
- 'method.get': 'GET',
- 'method.post': 'POST',
- 'method.put': 'PUT',
- 'method.delete': 'DELETE',
- 'method.patch': 'PATCH',
+ 'monitor.method.get': 'GET',
+ 'monitor.method.post': 'POST',
+ 'monitor.method.put': 'PUT',
+ 'monitor.method.delete': 'DELETE',
+ 'monitor.method.patch': 'PATCH',
// ============ Protokollstufen ============
- 'level.debug': 'DEBUG',
- 'level.info': 'INFO',
- 'level.warning': 'WARNING',
- 'level.error': 'ERROR',
- 'level.critical': 'CRITICAL',
+ 'monitor.level.debug': 'DEBUG',
+ 'monitor.level.info': 'INFO',
+ 'monitor.level.warning': 'WARNING',
+ 'monitor.level.error': 'ERROR',
+ 'monitor.level.critical': 'CRITICAL',
// ============ Planer ============
- 'scheduler.rule': 'Planungsregel',
- 'scheduler.last_run': 'Letzte Ausführung',
- 'scheduler.max_time': 'Max. Zeit',
- 'scheduler.never_run': 'Nie ausgeführt',
- 'scheduler.default': 'Standard',
- 'scheduler.running': 'Läuft',
- 'scheduler.not_scheduled': 'Nicht geplant',
+ 'monitor.scheduler.rule': 'Planungsregel',
+ 'monitor.scheduler.last_run': 'Letzte Ausführung',
+ 'monitor.scheduler.max_time': 'Max. Zeit',
+ 'monitor.scheduler.never_run': 'Nie ausgeführt',
+ 'monitor.scheduler.default': 'Standard',
+ 'monitor.scheduler.running': 'Läuft',
+ 'monitor.scheduler.not_scheduled': 'Nicht geplant',
// ============ Relative Zeit ============
- 'time.just_now': 'Gerade eben',
- 'time.minutes_ago': 'vor {n} Min.',
- 'time.hours_ago': 'vor {n} Stunden',
- 'time.days_ago': 'vor {n} Tagen',
- 'time.day_before_yesterday': 'Vorgestern',
+ 'monitor.time.just_now': 'Gerade eben',
+ 'monitor.time.minutes_ago': 'vor {n} Min.',
+ 'monitor.time.hours_ago': 'vor {n} Stunden',
+ 'monitor.time.days_ago': 'vor {n} Tagen',
+ 'monitor.time.day_before_yesterday': 'Vorgestern',
// ============ Warnungstypen ============
- 'alert.warning': 'Warnung',
- 'alert.error': 'Fehler',
- 'alert.critical': 'Kritisch',
- 'alert.normal': 'Normal',
+ 'monitor.alert.warning': 'Warnung',
+ 'monitor.alert.error': 'Fehler',
+ 'monitor.alert.critical': 'Kritisch',
+ 'monitor.alert.normal': 'Normal',
// ============ Verbindungsstatus ============
- 'connection.failed': 'Verbindung fehlgeschlagen',
- 'connection.reconnecting': 'Verbinde neu',
- 'connection.connected': 'Verbunden',
- 'connection.disconnected': 'Getrennt',
+ 'monitor.connection.failed': 'Verbindung fehlgeschlagen',
+ 'monitor.connection.reconnecting': 'Verbinde neu',
+ 'monitor.connection.connected': 'Verbunden',
+ 'monitor.connection.disconnected': 'Getrennt',
// ============ Überwachungsstatus ============
- 'monitor.paused': '● Überwachung pausiert (inaktiv)',
- 'monitor.panel': 'Überwachungstafel',
- 'monitor.last_update': 'Letzte Aktualisierung',
- 'monitor.reset_stats_confirm': 'Alle Ereignisstatistiken zurücksetzen?',
+ 'monitor.monitor.paused': '● Überwachung pausiert (inaktiv)',
+ 'monitor.monitor.panel': 'Überwachungstafel',
+ 'monitor.monitor.last_update': 'Letzte Aktualisierung',
+ 'monitor.monitor.reset_stats_confirm': 'Alle Ereignisstatistiken zurücksetzen?',
// ============ Hervorhebung ============
- 'highlight.on': 'Hervorgehoben',
- 'highlight.off': 'Hervorheben',
+ 'monitor.highlight.on': 'Hervorgehoben',
+ 'monitor.highlight.off': 'Hervorheben',
// ============ Kopieren ============
- 'copy.success': 'Kopiert',
- 'copy.failed': 'Kopieren fehlgeschlagen, bitte manuell kopieren',
- 'copy.error': 'Kopieren fehlgeschlagen',
+ 'monitor.copy.success': 'Kopiert',
+ 'monitor.copy.failed': 'Kopieren fehlgeschlagen, bitte manuell kopieren',
+ 'monitor.copy.error': 'Kopieren fehlgeschlagen',
// ============ Eingabeaufforderungen ============
- 'prompt.select_date': 'Bitte Datum auswählen',
- 'prompt.fetch_failed': 'Anfragedatensätze abrufen fehlgeschlagen, bitte erneut versuchen',
- 'prompt.fetch_outbound_failed': 'Ausgehende Anfragedatensätze abrufen fehlgeschlagen, bitte erneut versuchen',
- 'prompt.reset_failed': 'Zurücksetzen fehlgeschlagen, bitte erneut versuchen',
+ 'monitor.prompt.select_date': 'Bitte Datum auswählen',
+ 'monitor.prompt.fetch_failed': 'Anfragedatensätze abrufen fehlgeschlagen, bitte erneut versuchen',
+ 'monitor.prompt.fetch_outbound_failed': 'Ausgehende Anfragedatensätze abrufen fehlgeschlagen, bitte erneut versuchen',
+ 'monitor.prompt.reset_failed': 'Zurücksetzen fehlgeschlagen, bitte erneut versuchen',
// ============ Seitentitel ============
- 'page.http_requests_log': 'HTTP-Anfragedatensätze',
- 'page.outbound_requests_log': 'Ausgehende Anfragedatensätze',
+ 'monitor.page.http_requests_log': 'HTTP-Anfragedatensätze',
+ 'monitor.page.outbound_requests_log': 'Ausgehende Anfragedatensätze',
// ============ Zeit ============
- 'time.tomorrow': 'Morgen',
- 'time.day_after_tomorrow': 'Übermorgen',
- 'time.over_24h': 'Über 24 Stunden',
- 'time.expired': 'Abgelaufen',
- 'time.days_hours_later': 'In {days}T {hours}Std.',
- 'time.unknown': 'Unbekannt',
- 'time.month_day': '{day}.{month}.',
+ 'monitor.time.tomorrow': 'Morgen',
+ 'monitor.time.day_after_tomorrow': 'Übermorgen',
+ 'monitor.time.over_24h': 'Über 24 Stunden',
+ 'monitor.time.expired': 'Abgelaufen',
+ 'monitor.time.days_hours_later': 'In {days}T {hours}Std.',
+ 'monitor.time.unknown': 'Unbekannt',
+ 'monitor.time.month_day': '{day}.{month}.',
// ============ Aufgabentypen ============
- 'task.system': 'System',
- 'task.project': 'Projekt',
- 'task.avg_time': 'Durchschn. Ausführungszeit',
+ 'monitor.task.system': 'System',
+ 'monitor.task.project': 'Projekt',
+ 'monitor.task.avg_time': 'Durchschn. Ausführungszeit',
// ============ Detail-Modal ============
- 'modal.outbound_detail': 'Ausgehende Anfrage-Details',
- 'modal.timestamp': 'Zeitstempel:',
- 'modal.method': 'Methode:',
- 'modal.status_code': 'Statuscode:',
- 'modal.response_time': 'Antwortzeit:',
- 'modal.module': 'Modul:',
- 'modal.error_msg': 'Fehlermeldung:',
+ 'monitor.modal.outbound_detail': 'Ausgehende Anfrage-Details',
+ 'monitor.modal.timestamp': 'Zeitstempel:',
+ 'monitor.modal.method': 'Methode:',
+ 'monitor.modal.status_code': 'Statuscode:',
+ 'monitor.modal.response_time': 'Antwortzeit:',
+ 'monitor.modal.module': 'Modul:',
+ 'monitor.modal.error_msg': 'Fehlermeldung:',
// ============ Anfrage/Antwort-Bereiche ============
- 'section.request_headers': 'Anfrage-Header',
- 'section.request_body': 'Anfrage-Body',
- 'section.response_headers': 'Antwort-Header',
- 'section.response_body': 'Antwort-Body',
- 'section.no_response': 'Keine Antwort-Body-Daten',
- 'section.response_failed': 'Antwort-Body-Verarbeitung fehlgeschlagen: ',
- 'section.truncated': '[Inhalt gekürzt, exportieren für vollständigen Inhalt]',
- 'section.large_data_warning': '⚠️ Große Datenmenge ({count} Zeichen), kann Leistung beeinträchtigen',
- 'section.showing_chars': 'Zeige erste {count} Zeichen',
- 'section.basic_info': 'Grundinformationen',
- 'section.no_data': 'Keine Daten',
+ 'monitor.section.request_headers': 'Anfrage-Header',
+ 'monitor.section.request_body': 'Anfrage-Body',
+ 'monitor.section.response_headers': 'Antwort-Header',
+ 'monitor.section.response_body': 'Antwort-Body',
+ 'monitor.section.no_response': 'Keine Antwort-Body-Daten',
+ 'monitor.section.response_failed': 'Antwort-Body-Verarbeitung fehlgeschlagen: ',
+ 'monitor.section.truncated': '[Inhalt gekürzt, exportieren für vollständigen Inhalt]',
+ 'monitor.section.large_data_warning': '⚠️ Große Datenmenge ({count} Zeichen), kann Leistung beeinträchtigen',
+ 'monitor.section.showing_chars': 'Zeige erste {count} Zeichen',
+ 'monitor.section.basic_info': 'Grundinformationen',
+ 'monitor.section.no_data': 'Keine Daten',
// ============ Ratenbegrenzung ============
- 'status.rate_limited': 'Ratenbegrenzt',
+ 'monitor.status.rate_limited': 'Ratenbegrenzt',
// ============ Protokollstufen-Auswahl ============
- 'log_level.all_with_debug': 'Alle Stufen (mit DEBUG)',
- 'log_level.error': 'Fehlerprotokolle',
- 'log_level.warning': 'Warnungsprotokolle',
- 'log_level.info': 'Infoprotokolle',
- 'log_level.debug': 'Debugprotokolle',
+ 'monitor.log_level.all_with_debug': 'Alle Stufen (mit DEBUG)',
+ 'monitor.log_level.error': 'Fehlerprotokolle',
+ 'monitor.log_level.warning': 'Warnungsprotokolle',
+ 'monitor.log_level.info': 'Infoprotokolle',
+ 'monitor.log_level.debug': 'Debugprotokolle',
// ============ DeadLetter ============
- 'dl.reprocess': 'Neu verarbeiten',
- 'dl.reprocess_success': 'DeadLetter erfolgreich neu verarbeitet',
- 'dl.reprocess_failed': 'DeadLetter Neuverarbeitung fehlgeschlagen',
+ 'monitor.dl.reprocess': 'Neu verarbeiten',
+ 'monitor.dl.reprocess_success': 'DeadLetter erfolgreich neu verarbeitet',
+ 'monitor.dl.reprocess_failed': 'DeadLetter Neuverarbeitung fehlgeschlagen',
// ============ Backend-Warnmeldungen-Zuordnung ============
- 'alert.scheduler_not_running': 'Planer läuft nicht',
- 'alert.db_connection_failed': 'Datenbankverbindung fehlgeschlagen',
- 'alert.redis_connection_failed': 'Redis-Verbindung fehlgeschlagen',
- 'alert.event_listener_stopped': 'Ereignis-Listener gestoppt',
- 'alert.binlog_listener_stopped': 'Binlog-Listener gestoppt',
- 'alert.high_error_rate': 'Hohe Fehlerrate',
- 'alert.memory_warning': 'Speichernutzung-Warnung',
- 'alert.cpu_warning': 'CPU-Nutzung-Warnung',
+ 'monitor.alert.scheduler_not_running': 'Planer läuft nicht',
+ 'monitor.alert.db_connection_failed': 'Datenbankverbindung fehlgeschlagen',
+ 'monitor.alert.redis_connection_failed': 'Redis-Verbindung fehlgeschlagen',
+ 'monitor.alert.event_listener_stopped': 'Ereignis-Listener gestoppt',
+ 'monitor.alert.binlog_listener_stopped': 'Binlog-Listener gestoppt',
+ 'monitor.alert.high_error_rate': 'Hohe Fehlerrate',
+ 'monitor.alert.memory_warning': 'Speichernutzung-Warnung',
+ 'monitor.alert.cpu_warning': 'CPU-Nutzung-Warnung',
// ================================================================================
// MDS-Modul (Daten-Bereitstellungssystem)
@@ -659,5 +669,12 @@ window.__i18n_de_DE__ = {
'mds.other.tip': 'Hinweis',
'mds.other.warning': 'Warnung',
'mds.other.error': 'Fehler',
- 'mds.other.success': 'Erfolg'
+ 'mds.other.success': 'Erfolg',
+
+ // ============ 补充翻译键 ============
+ 'monitor.other.yes': 'Ja',
+ 'monitor.other.no': 'Nein',
+ 'monitor.other.data_points': 'Datenpunkte',
+ 'monitor.col.is_slow': 'Ist langsame Anfrage',
+ 'monitor.col.is_error': 'Ist Fehler'
};
diff --git a/static/lib/i18n/en-US.js b/static/lib/i18n/en-US.js
index 6c7124c..4acd29d 100644
--- a/static/lib/i18n/en-US.js
+++ b/static/lib/i18n/en-US.js
@@ -3,497 +3,507 @@
*/
window.__i18n_en_US__ = {
// ============ Page Titles ============
- 'page.title': 'MyAPI System Monitor',
- 'page.live_logs': 'Live Logs - System Monitor',
- 'page.history_logs': 'Log History Query - System Monitor',
+ 'monitor.page.title': 'MyAPI System Monitor',
+ 'monitor.page.live_logs': 'Live Logs - System Monitor',
+ 'monitor.page.history_logs': 'Log History Query - System Monitor',
// ============ Navigation ============
- 'nav.overview': '📊 Overview',
- 'nav.database': '🗃️ Database',
- 'nav.events': '☎️ Events',
- 'nav.scheduler': '⏰ Scheduler',
- 'nav.http_requests': '📥 HTTP Requests',
- 'nav.outbound_requests': '📤 Outbound',
- 'nav.logs': '📋 Logs',
+ 'monitor.nav.overview': '📊 Overview',
+ 'monitor.nav.database': '🗃️ Database',
+ 'monitor.nav.events': '☎️ Events',
+ 'monitor.nav.scheduler': '⏰ Scheduler',
+ 'monitor.nav.http_requests': '📥 HTTP Requests',
+ 'monitor.nav.outbound_requests': '📤 Outbound',
+ 'monitor.nav.logs': '📋 Logs',
// ============ Tabs ============
- 'tab.overview': 'Overview',
- 'tab.database': 'Database',
- 'tab.events': 'Events',
- 'tab.scheduler': 'Scheduler',
- 'tab.http': 'HTTP Requests',
- 'tab.outbound': 'Outbound',
- 'tab.logs': 'System Logs',
- 'tab.timeline': 'Timeline',
- 'tab.chart': 'Charts',
+ 'monitor.tab.overview': 'Overview',
+ 'monitor.tab.database': 'Database',
+ 'monitor.tab.events': 'Events',
+ 'monitor.tab.scheduler': 'Scheduler',
+ 'monitor.tab.http': 'HTTP Requests',
+ 'monitor.tab.outbound': 'Outbound',
+ 'monitor.tab.logs': 'System Logs',
+ 'monitor.tab.timeline': 'Timeline',
+ 'monitor.tab.chart': 'Charts',
// ============ Cards ============
- 'card.resource': 'Resource Usage',
- 'card.db_status': 'Database Status',
- 'card.db_connections': 'Database Connections',
- 'card.event_helpers': 'Event Helpers',
- 'card.scheduler': 'Job Scheduler',
- 'card.api_requests': 'HTTP Requests',
- 'card.outbound_requests': 'Outbound Requests',
- 'card.redis': 'Redis Status',
- 'card.event_listener': 'Event Listener',
- 'card.recent_alerts': 'Recent Alerts',
- 'card.mysql': 'MySQL',
- 'card.http_requests_log': 'HTTP Request Records',
- 'card.scheduler_detail': 'Scheduler Details',
- 'card.outbound_requests_log': 'Outbound Request Records',
- 'card.callback_tracker': 'Callback Tracker',
- 'card.event_deduplicator': 'Event Deduplicator',
+ 'monitor.card.resource': 'Resource Usage',
+ 'monitor.card.db_status': 'Database Status',
+ 'monitor.card.db_connections': 'Database Connections',
+ 'monitor.card.event_helpers': 'Event Helpers',
+ 'monitor.card.scheduler': 'Job Scheduler',
+ 'monitor.card.api_requests': 'HTTP Requests',
+ 'monitor.card.outbound_requests': 'Outbound Requests',
+ 'monitor.card.redis': 'Redis Status',
+ 'monitor.card.event_listener': 'Event Listener',
+ 'monitor.card.recent_alerts': 'Recent Alerts',
+ 'monitor.card.mysql': 'MySQL',
+ 'monitor.card.http_requests_log': 'HTTP Request Records',
+ 'monitor.card.scheduler_detail': 'Scheduler Details',
+ 'monitor.card.outbound_requests_log': 'Outbound Request Records',
+ 'monitor.card.callback_tracker': 'Callback Tracker',
+ 'monitor.card.event_deduplicator': 'Event Deduplicator',
// ============ Metrics ============
- 'metric.cpu': 'CPU Usage',
- 'metric.memory': 'Memory Usage',
- 'metric.threads': 'Threads',
- 'metric.uptime': 'Uptime',
- 'metric.total_connections': 'Total Connections',
- 'metric.healthy': 'Healthy',
- 'metric.unhealthy': 'Unhealthy',
- 'metric.degraded': 'Degraded',
- 'metric.total': 'Total',
- 'metric.success': 'Success',
- 'metric.failed': 'Failed',
- 'metric.pending': 'Pending',
- 'metric.active': 'Active',
- 'metric.idle': 'Idle',
- 'metric.unknown': 'Unknown',
- 'metric.warning': 'Warning',
- 'metric.events_received': 'Events Received',
- 'metric.events_processed': 'Events Processed',
- 'metric.events_failed': 'Events Failed',
- 'metric.jobs_running': 'Running Jobs',
- 'metric.jobs_pending': 'Pending Jobs',
- 'metric.requests_total': 'Total Requests',
- 'metric.requests_slow': 'Slow Requests',
- 'metric.requests_error': 'Error Requests',
- 'metric.error_rate': 'Error Rate',
- 'metric.avg_time': 'Avg Response',
- 'metric.connection_status': 'Connection Status',
- 'metric.host': 'Host',
- 'metric.port': 'Port',
- 'metric.database': 'Database',
- 'metric.used_connections': 'Used Connections',
- 'metric.max_connections': 'Max Connections',
- 'metric.connection_usage': 'Connection Usage',
- 'metric.buffer_size': 'Buffer Size',
- 'metric.buffer_threshold': 'Buffer Threshold',
- 'metric.buffer_usage': 'Buffer Usage',
- 'metric.events_interrupted': 'Interrupted',
- 'metric.overall_success_rate': 'Overall Success Rate',
- 'metric.active_event_types': 'Active Event Types',
- 'metric.backpressure_status': 'Backpressure Status',
- 'metric.backpressure_pending': 'Pending Events',
- 'metric.backpressure_usage': 'Backpressure Usage',
- 'metric.event_loop_status': 'Event Loop Status',
- 'metric.pending_callbacks': 'Pending Callbacks',
- 'metric.max_retries': 'Max Retries',
- 'metric.pending_retries': 'Pending Retries',
- 'metric.total_entries': 'Total Entries',
- 'metric.active_items': 'Active Items',
- 'metric.ttl_seconds': 'TTL (seconds)',
- 'metric.max_entries': 'Max Entries',
- 'metric.dl_total': 'DL Total',
- 'metric.recent_dl': 'Recent DL',
- 'metric.process_success_rate': 'Process Success Rate',
- 'metric.scheduler_status': 'Scheduler Status',
- 'metric.job_count': 'Job Count',
- 'metric.pending_events': 'Pending Events',
- 'metric.total_events': 'Total Events',
- 'metric.file_size': 'File Size',
- 'metric.running_status': 'Running Status',
+ 'monitor.metric.cpu': 'CPU Usage',
+ 'monitor.metric.memory': 'Memory Usage',
+ 'monitor.metric.threads': 'Threads',
+ 'monitor.metric.uptime': 'Uptime',
+ 'monitor.metric.total_connections': 'Total Connections',
+ 'monitor.metric.healthy': 'Healthy',
+ 'monitor.metric.unhealthy': 'Unhealthy',
+ 'monitor.metric.degraded': 'Degraded',
+ 'monitor.metric.total': 'Total',
+ 'monitor.metric.success': 'Success',
+ 'monitor.metric.failed': 'Failed',
+ 'monitor.metric.pending': 'Pending',
+ 'monitor.metric.active': 'Active',
+ 'monitor.metric.idle': 'Idle',
+ 'monitor.metric.unknown': 'Unknown',
+ 'monitor.metric.warning': 'Warning',
+ 'monitor.metric.events_received': 'Events Received',
+ 'monitor.metric.events_processed': 'Events Processed',
+ 'monitor.metric.events_failed': 'Events Failed',
+ 'monitor.metric.jobs_running': 'Running Jobs',
+ 'monitor.metric.jobs_pending': 'Pending Jobs',
+ 'monitor.metric.requests_total': 'Total Requests',
+ 'monitor.metric.requests_slow': 'Slow Requests',
+ 'monitor.metric.requests_error': 'Error Requests',
+ 'monitor.metric.error_rate': 'Error Rate',
+ 'monitor.metric.avg_time': 'Avg Response',
+ 'monitor.metric.connection_status': 'Connection Status',
+ 'monitor.metric.host': 'Host',
+ 'monitor.metric.port': 'Port',
+ 'monitor.metric.database': 'Database',
+ 'monitor.metric.used_connections': 'Used Connections',
+ 'monitor.metric.max_connections': 'Max Connections',
+ 'monitor.metric.connection_usage': 'Connection Usage',
+ 'monitor.metric.buffer_size': 'Buffer Size',
+ 'monitor.metric.buffer_threshold': 'Buffer Threshold',
+ 'monitor.metric.buffer_usage': 'Buffer Usage',
+ 'monitor.metric.events_interrupted': 'Interrupted',
+ 'monitor.metric.overall_success_rate': 'Overall Success Rate',
+ 'monitor.metric.active_event_types': 'Active Event Types',
+ 'monitor.metric.backpressure_status': 'Backpressure Status',
+ 'monitor.metric.backpressure_pending': 'Pending Events',
+ 'monitor.metric.backpressure_usage': 'Backpressure Usage',
+ 'monitor.metric.event_loop_status': 'Event Loop Status',
+ 'monitor.metric.pending_callbacks': 'Pending Callbacks',
+ 'monitor.metric.max_retries': 'Max Retries',
+ 'monitor.metric.pending_retries': 'Pending Retries',
+ 'monitor.metric.total_entries': 'Total Entries',
+ 'monitor.metric.active_items': 'Active Items',
+ 'monitor.metric.ttl_seconds': 'TTL (seconds)',
+ 'monitor.metric.max_entries': 'Max Entries',
+ 'monitor.metric.dl_total': 'DL Total',
+ 'monitor.metric.recent_dl': 'Recent DL',
+ 'monitor.metric.process_success_rate': 'Process Success Rate',
+ 'monitor.metric.scheduler_status': 'Scheduler Status',
+ 'monitor.metric.job_count': 'Job Count',
+ 'monitor.metric.pending_events': 'Pending Events',
+ 'monitor.metric.total_events': 'Total Events',
+ 'monitor.metric.file_size': 'File Size',
+ 'monitor.metric.running_status': 'Running Status',
// ============ Status ============
- 'status.healthy': '● System Healthy',
- 'status.unhealthy': '● System Unhealthy',
- 'status.degraded': '● System Degraded',
- 'status.running': 'Running',
- 'status.checking': 'Checking',
- 'status.stopped': 'Stopped',
- 'status.loading': 'Loading...',
- 'status.querying': 'Querying...',
- 'status.exporting': 'Exporting...',
- 'status.no_data': 'No data',
- 'status.connected': 'Connected',
- 'status.connecting': 'Connecting...',
- 'status.disconnected': 'Disconnected',
- 'status.reconnecting': 'Reconnecting...',
- 'status.paused': 'Paused',
- 'status.enabled': 'Enabled',
- 'status.disabled': 'Disabled',
- 'status.no_db_connections': 'No database connections',
- 'status.no_scheduler': 'No scheduled tasks',
- 'status.no_http_requests': 'No HTTP requests',
- 'status.no_alerts': 'No alerts',
- 'status.no_logs': 'No logs',
- 'status.no_events': 'No event statistics',
- 'status.no_api_requests': 'No API request records',
- 'status.no_outbound_requests': 'No outbound request records',
- 'status.no_dead_letters': 'No DL',
- 'status.partial_warnings': '● Partial Warnings',
- 'status.monitoring': 'Monitoring',
+ 'monitor.status.healthy': '● System Healthy',
+ 'monitor.status.unhealthy': '● System Unhealthy',
+ 'monitor.status.degraded': '● System Degraded',
+ 'monitor.status.running': 'Running',
+ 'monitor.status.checking': 'Checking',
+ 'monitor.status.stopped': 'Stopped',
+ 'monitor.status.loading': 'Loading...',
+ 'monitor.status.querying': 'Querying...',
+ 'monitor.status.exporting': 'Exporting...',
+ 'monitor.status.no_data': 'No data',
+ 'monitor.status.connected': 'Connected',
+ 'monitor.status.connecting': 'Connecting...',
+ 'monitor.status.disconnected': 'Disconnected',
+ 'monitor.status.reconnecting': 'Reconnecting...',
+ 'monitor.status.paused': 'Paused',
+ 'monitor.status.enabled': 'Enabled',
+ 'monitor.status.disabled': 'Disabled',
+ 'monitor.status.no_db_connections': 'No database connections',
+ 'monitor.status.no_scheduler': 'No scheduled tasks',
+ 'monitor.status.no_http_requests': 'No HTTP requests',
+ 'monitor.status.no_alerts': 'No alerts',
+ 'monitor.status.no_logs': 'No logs',
+ 'monitor.status.no_events': 'No event statistics',
+ 'monitor.status.no_api_requests': 'No API request records',
+ 'monitor.status.no_outbound_requests': 'No outbound request records',
+ 'monitor.status.no_dead_letters': 'No DL',
+ 'monitor.status.partial_warnings': '● Partial Warnings',
+ 'monitor.status.monitoring': 'Monitoring',
// ============ Buttons ============
- 'btn.query': 'Query',
- 'btn.reset': 'Reset',
- 'btn.refresh': 'Refresh',
- 'btn.export': 'Export',
- 'btn.export_csv': 'Export CSV',
- 'btn.export_json': 'Export JSON',
- 'btn.detail': 'Detail',
- 'btn.pause': 'Pause',
- 'btn.resume': 'Resume',
- 'btn.clear': 'Clear',
- 'btn.save': 'Save',
- 'btn.delete': 'Delete',
- 'btn.close': 'Close',
- 'btn.confirm': 'Confirm',
- 'btn.cancel': 'Cancel',
- 'btn.test': 'Test',
- 'btn.apply': 'Apply',
- 'btn.filter': 'Filter',
- 'btn.clear_all': 'Clear All',
- 'btn.realtime_on': 'Realtime: ON',
- 'btn.realtime_off': 'Realtime: OFF',
- 'btn.precise_locate': '± 60s',
- 'btn.precise_cancel': '± 60s - Click to cancel',
- 'btn.reset_stats': 'Reset Stats',
- 'btn.flush_all': 'Flush All',
- 'btn.new_window': 'New Window',
- 'btn.auto_scroll': 'Auto Scroll',
- 'btn.live_logs': 'Live Logs',
- 'btn.history_query': 'History Query',
- 'btn.mark_all_read': 'Mark All Read',
- 'btn.clear_read_status': 'Clear Read Status',
+ 'monitor.btn.query': 'Query',
+ 'monitor.btn.reset': 'Reset',
+ 'monitor.btn.refresh': 'Refresh',
+ 'monitor.btn.export': 'Export',
+ 'monitor.btn.export_csv': 'Export CSV',
+ 'monitor.btn.export_json': 'Export JSON',
+ 'monitor.btn.detail': 'Detail',
+ 'monitor.btn.pause': 'Pause',
+ 'monitor.btn.resume': 'Resume',
+ 'monitor.btn.clear': 'Clear',
+ 'monitor.btn.save': 'Save',
+ 'monitor.btn.delete': 'Delete',
+ 'monitor.btn.close': 'Close',
+ 'monitor.btn.confirm': 'Confirm',
+ 'monitor.btn.cancel': 'Cancel',
+ 'monitor.btn.test': 'Test',
+ 'monitor.btn.apply': 'Apply',
+ 'monitor.btn.filter': 'Filter',
+ 'monitor.btn.clear_all': 'Clear All',
+ 'monitor.btn.realtime_on': 'Realtime: ON',
+ 'monitor.btn.realtime_off': 'Realtime: OFF',
+ 'monitor.btn.precise_locate': '± 60s',
+ 'monitor.btn.precise_cancel': '± 60s - Click to cancel',
+ 'monitor.btn.reset_stats': 'Reset Stats',
+ 'monitor.btn.flush_all': 'Flush All',
+ 'monitor.btn.new_window': 'New Window',
+ 'monitor.btn.auto_scroll': 'Auto Scroll',
+ 'monitor.btn.live_logs': 'Live Logs',
+ 'monitor.btn.history_query': 'History Query',
+ 'monitor.btn.mark_all_read': 'Mark All Read',
+ 'monitor.btn.clear_read_status': 'Clear Read Status',
// ============ Table Columns ============
- 'col.index': '#',
- 'col.time': 'Time',
- 'col.level': 'Level',
- 'col.module': 'Module',
- 'col.message': 'Message',
- 'col.method': 'Method',
- 'col.path': 'Path',
- 'col.url': 'URL',
- 'col.status': 'Status',
- 'col.duration': 'Duration',
- 'col.client_ip': 'Client IP',
- 'col.source': 'Source',
- 'col.function': 'Function',
- 'col.line': 'Line',
- 'col.operation': 'Action',
- 'col.count': 'Count',
- 'col.avg_time': 'Avg Time',
- 'col.max_time': 'Max Time',
- 'col.db_name': 'DB Name',
- 'col.last_check': 'Last Check',
- 'col.current_connections': 'Current',
- 'col.max_connections': 'Max',
- 'col.min_connections': 'Min',
- 'col.idle_connections': 'Idle',
- 'col.used_connections': 'Used',
- 'col.usage': 'Usage',
- 'col.processed_records': 'Processed',
- 'col.timestamp': 'Timestamp',
- 'col.query_params': 'Query Params',
- 'col.error_message': 'Error Message',
- 'col.url': 'URL',
- 'col.is_read': 'Read',
- 'col.description': 'Description',
- 'col.total_received': 'Total Received',
- 'col.pending': 'Pending',
- 'col.processed': 'Processed',
- 'col.interrupted': 'Interrupted',
- 'col.completion_rate': 'Completion Rate',
- 'col.avg_latency': 'Avg Latency',
- 'col.last_action': 'Last Action',
- 'col.id': 'ID',
- 'col.event_type': 'Event Type',
- 'col.table': 'Table',
+ 'monitor.col.index': '#',
+ 'monitor.col.time': 'Time',
+ 'monitor.col.level': 'Level',
+ 'monitor.col.module': 'Module',
+ 'monitor.col.message': 'Message',
+ 'monitor.col.method': 'Method',
+ 'monitor.col.path': 'Path',
+ 'monitor.col.url': 'URL',
+ 'monitor.col.status': 'Status',
+ 'monitor.col.duration': 'Duration',
+ 'monitor.col.client_ip': 'Client IP',
+ 'monitor.col.source': 'Source',
+ 'monitor.col.function': 'Function',
+ 'monitor.col.line': 'Line',
+ 'monitor.col.operation': 'Action',
+ 'monitor.col.count': 'Count',
+ 'monitor.col.avg_time': 'Avg Time',
+ 'monitor.col.max_time': 'Max Time',
+ 'monitor.col.db_name': 'DB Name',
+ 'monitor.col.last_check': 'Last Check',
+ 'monitor.col.current_connections': 'Current',
+ 'monitor.col.max_connections': 'Max',
+ 'monitor.col.min_connections': 'Min',
+ 'monitor.col.idle_connections': 'Idle',
+ 'monitor.col.used_connections': 'Used',
+ 'monitor.col.usage': 'Usage',
+ 'monitor.col.processed_records': 'Processed',
+ 'monitor.col.timestamp': 'Timestamp',
+ 'monitor.col.query_params': 'Query Params',
+ 'monitor.col.error_message': 'Error Message',
+ 'monitor.col.url': 'URL',
+ 'monitor.col.is_read': 'Read',
+ 'monitor.col.description': 'Description',
+ 'monitor.col.total_received': 'Total Received',
+ 'monitor.col.pending': 'Pending',
+ 'monitor.col.processed': 'Processed',
+ 'monitor.col.interrupted': 'Interrupted',
+ 'monitor.col.completion_rate': 'Completion Rate',
+ 'monitor.col.avg_latency': 'Avg Latency',
+ 'monitor.col.last_action': 'Last Action',
+ 'monitor.col.id': 'ID',
+ 'monitor.col.event_type': 'Event Type',
+ 'monitor.col.table': 'Table',
// ============ Quick Time ============
- 'time.start_time': 'Start Time',
- 'time.end_time': 'End Time',
- 'time.last_10m': 'Last 10 min',
- 'time.last_30m': 'Last 30 min',
- 'time.last_1h': 'Last 1 hour',
- 'time.last_6h': 'Last 6 hours',
- 'time.last_24h': 'Last 24 hours',
- 'time.range': 'Time Range',
+ 'monitor.time.start_time': 'Start Time',
+ 'monitor.time.end_time': 'End Time',
+ 'monitor.time.last_10m': 'Last 10 min',
+ 'monitor.time.last_30m': 'Last 30 min',
+ 'monitor.time.last_1h': 'Last 1 hour',
+ 'monitor.time.last_6h': 'Last 6 hours',
+ 'monitor.time.last_24h': 'Last 24 hours',
+ 'monitor.time.range': 'Time Range',
// ============ Filters ============
- 'filter.level': 'All Levels',
- 'filter.type': 'All Types',
- 'filter.module': 'Module',
- 'filter.keyword': 'Keyword',
- 'filter.method': 'Method',
- 'filter.client_ip': 'Client IP',
- 'filter.status_range': 'Status Range',
- 'filter.duration_range': 'Duration Range',
- 'filter.advanced': 'Advanced Filters',
- 'filter.collapse': 'Collapse',
- 'filter.expand': 'Expand',
- 'filter.clear': 'Clear',
- 'filter.active': 'Active Filters',
- 'filter.module_placeholder': 'Enter module name',
- 'filter.keyword_placeholder': 'Enter keyword',
- 'filter.ip_placeholder': 'Enter client IP',
- 'filter.status_placeholder': 'e.g.: 400-499',
- 'filter.duration_placeholder': 'e.g.: >1000',
- 'filter.all_logs': 'All Logs',
+ 'monitor.filter.level': 'All Levels',
+ 'monitor.filter.type': 'All Types',
+ 'monitor.filter.module': 'Module',
+ 'monitor.filter.keyword': 'Keyword',
+ 'monitor.filter.method': 'Method',
+ 'monitor.filter.client_ip': 'Client IP',
+ 'monitor.filter.status_range': 'Status Range',
+ 'monitor.filter.duration_range': 'Duration Range',
+ 'monitor.filter.advanced': 'Advanced Filters',
+ 'monitor.filter.collapse': 'Collapse',
+ 'monitor.filter.expand': 'Expand',
+ 'monitor.filter.clear': 'Clear',
+ 'monitor.filter.active': 'Active Filters',
+ 'monitor.filter.module_placeholder': 'Enter module name',
+ 'monitor.filter.keyword_placeholder': 'Enter keyword',
+ 'monitor.filter.ip_placeholder': 'Enter client IP',
+ 'monitor.filter.status_placeholder': 'e.g.: 400-499',
+ 'monitor.filter.duration_placeholder': 'e.g.: >1000',
+ 'monitor.filter.all_logs': 'All Logs',
// ============ Charts ============
- 'chart.request_trend': '📊 Request Trend',
- 'chart.level_distribution': '📊 Log Level Distribution',
- 'chart.status_distribution': '📈 Status Code Distribution',
- 'chart.slow_requests': '⏱️ Slow Requests TOP10',
- 'chart.total_requests': 'Total Requests',
- 'chart.error_count': 'Errors',
- 'chart.slow_count': 'Slow Requests',
- 'chart.log_count': 'Total Logs',
- 'chart.no_slow': '✅ No slow requests',
- 'chart.query_required': 'Please execute query first',
- 'chart.load_failed': '❌ Chart data load failed, please retry',
- 'chart.redis_connections': 'Redis Connection Pool Usage',
- 'chart.redis_buffer': 'Redis Buffer Size Changes',
- 'chart.cpu': 'CPU',
- 'chart.memory': 'Memory',
- 'chart.upload': 'Upload',
- 'chart.download': 'Download',
- 'chart.cpu_memory_axis': 'CPU / Memory (%)',
- 'chart.network_axis': 'Network Upload / Download (KB/s)',
- 'chart.used_connections': 'Used Connections',
- 'chart.buffer_size_mb': 'Buffer Size (MB)',
+ 'monitor.chart.request_trend': '📊 Request Trend',
+ 'monitor.chart.level_distribution': '📊 Log Level Distribution',
+ 'monitor.chart.status_distribution': '📈 Status Code Distribution',
+ 'monitor.chart.slow_requests': '⏱️ Slow Requests TOP10',
+ 'monitor.chart.total_requests': 'Total Requests',
+ 'monitor.chart.error_count': 'Errors',
+ 'monitor.chart.slow_count': 'Slow Requests',
+ 'monitor.chart.log_count': 'Total Logs',
+ 'monitor.chart.no_slow': '✅ No slow requests',
+ 'monitor.chart.query_required': 'Please execute query first',
+ 'monitor.chart.load_failed': '❌ Chart data load failed, please retry',
+ 'monitor.chart.no_data': 'No data',
+ 'monitor.chart.no_trend': 'No trend data',
+ 'monitor.chart.redis_connections': 'Redis Connection Pool Usage',
+ 'monitor.chart.redis_buffer': 'Redis Buffer Size Changes',
+ 'monitor.chart.cpu': 'CPU',
+ 'monitor.chart.memory': 'Memory',
+ 'monitor.chart.upload': 'Upload',
+ 'monitor.chart.download': 'Download',
+ 'monitor.chart.cpu_memory_axis': 'CPU / Memory (%)',
+ 'monitor.chart.network_axis': 'Network Upload / Download (KB/s)',
+ 'monitor.chart.used_connections': 'Used Connections',
+ 'monitor.chart.buffer_size_mb': 'Buffer Size (MB)',
// ============ Errors ============
- 'error.time_range_invalid': 'Start time cannot be greater than end time',
- 'error.time_range_required': 'Please select start time and end time',
- 'error.query_failed': 'Query failed, please try again',
- 'error.export_failed': 'Export failed, please retry',
- 'error.connection_failed': 'Connection failed, please refresh page',
- 'error.max_templates': 'Maximum 10 templates allowed, please delete some first',
- 'error.auto_pause': 'Realtime tracking auto-paused (over 10 minutes)',
- 'error.load_failed': 'Load failed, please retry',
- 'error.invalid_params': 'Invalid parameters',
- 'error.clear_dl_failed': 'Failed to clear DeadLetter queue',
+ 'monitor.error.time_range_invalid': 'Start time cannot be greater than end time',
+ 'monitor.error.time_range_required': 'Please select start time and end time',
+ 'monitor.error.query_failed': 'Query failed, please try again',
+ 'monitor.error.export_failed': 'Export failed, please retry',
+ 'monitor.error.connection_failed': 'Connection failed, please refresh page',
+ 'monitor.error.max_templates': 'Maximum 10 templates allowed, please delete some first',
+ 'monitor.error.auto_pause': 'Realtime tracking auto-paused (over 10 minutes)',
+ 'monitor.error.load_failed': 'Load failed, please retry',
+ 'monitor.error.invalid_params': 'Invalid parameters',
+ 'monitor.error.clear_dl_failed': 'Failed to clear DeadLetter queue',
// ============ Success ============
- 'success.query_complete': 'Query Complete',
- 'success.export_complete': 'Export Complete',
- 'success.template_saved': 'Template "{name}" saved',
- 'success.logs_cleared': 'Logs cleared',
- 'success.operation_success': 'Operation successful',
- 'success.dl_cleared': 'DeadLetter queue cleared',
+ 'monitor.success.query_complete': 'Query Complete',
+ 'monitor.success.export_complete': 'Export Complete',
+ 'monitor.success.template_saved': 'Template "{name}" saved',
+ 'monitor.success.logs_cleared': 'Logs cleared',
+ 'monitor.success.operation_success': 'Operation successful',
+ 'monitor.success.dl_cleared': 'DeadLetter queue cleared',
// ============ Timeline ============
- 'timeline.title': 'Timeline',
- 'timeline.no_data': 'No data, please execute query first',
- 'timeline.anomaly_detected': '⚠️ {count} anomalies detected',
- 'timeline.error_burst': '{count} consecutive ERROR logs',
- 'timeline.slow_anomaly': 'Slow request anomaly ({duration}ms > avg {avg}ms×3)',
- 'timeline.duplicate_error': 'Duplicate error "{msg}" appeared {count} times',
- 'timeline.http_request': 'HTTP Request',
- 'timeline.outbound_request': 'Outbound Request',
- 'timeline.system_log': 'System Log',
+ 'monitor.timeline.title': 'Timeline',
+ 'monitor.timeline.no_data': 'No data, please execute query first',
+ 'monitor.timeline.anomaly_detected': '⚠️ {count} anomalies detected',
+ 'monitor.timeline.error_burst': '{count} consecutive ERROR logs',
+ 'monitor.timeline.slow_anomaly': 'Slow request anomaly ({duration}ms > avg {avg}ms×3)',
+ 'monitor.timeline.duplicate_error': 'Duplicate error "{msg}" appeared {count} times',
+ 'monitor.timeline.http_request': 'HTTP Request',
+ 'monitor.timeline.outbound_request': 'Outbound Request',
+ 'monitor.timeline.system_log': 'System Log',
+ 'monitor.timeline.summary_title': '📊 Timeline Statistics',
+ 'monitor.timeline.total_events': 'Total Events',
+ 'monitor.timeline.http_requests': 'HTTP Requests',
+ 'monitor.timeline.outbound_requests': 'Outbound Requests',
+ 'monitor.timeline.system_logs': 'System Logs',
+ 'monitor.timeline.errors': 'Errors',
+ 'monitor.timeline.warnings': 'Warnings',
+ 'monitor.timeline.slow_requests': 'Slow Requests',
// ============ Pagination ============
- 'pagination.page': 'Page',
- 'pagination.of': 'of',
- 'pagination.items': 'items',
- 'pagination.showing': 'Showing',
- 'pagination.per_page': 'Per Page',
- 'pagination.first': 'First',
- 'pagination.last': 'Last',
- 'pagination.prev': 'Previous',
- 'pagination.next': 'Next',
- 'pagination.go_to': 'Go to',
+ 'monitor.pagination.page': 'Page',
+ 'monitor.pagination.of': 'of',
+ 'monitor.pagination.items': 'items',
+ 'monitor.pagination.showing': 'Showing',
+ 'monitor.pagination.per_page': 'Per Page',
+ 'monitor.pagination.first': 'First',
+ 'monitor.pagination.last': 'Last',
+ 'monitor.pagination.prev': 'Previous',
+ 'monitor.pagination.next': 'Next',
+ 'monitor.pagination.go_to': 'Go to',
// ============ Export ============
- 'export.current_page': 'Export Current Page',
- 'export.all_data': 'Export All Data',
- 'export.format_csv': 'CSV Format',
- 'export.format_json': 'JSON Format',
- 'export.select_format': 'Select Format',
+ 'monitor.export.current_page': 'Export Current Page',
+ 'monitor.export.all_data': 'Export All Data',
+ 'monitor.export.format_csv': 'CSV Format',
+ 'monitor.export.format_json': 'JSON Format',
+ 'monitor.export.select_format': 'Select Format',
// ============ Templates ============
- 'template.saved_queries': 'Saved Queries...',
- 'template.save': 'Save Query',
- 'template.manage': 'Manage Templates',
- 'template.name': 'Template Name',
- 'template.description': 'Description',
- 'template.delete_confirm': 'Delete this template?',
- 'template.empty': 'No saved templates',
+ 'monitor.template.saved_queries': 'Saved Queries...',
+ 'monitor.template.save': 'Save Query',
+ 'monitor.template.manage': 'Manage Templates',
+ 'monitor.template.name': 'Template Name',
+ 'monitor.template.description': 'Description',
+ 'monitor.template.delete_confirm': 'Delete this template?',
+ 'monitor.template.empty': 'No saved templates',
// ============ Statistics ============
- 'stats.time_range': 'Query Time Range',
- 'stats.http_requests': 'HTTP Requests',
- 'stats.outbound_requests': 'Outbound Requests',
- 'stats.system_logs': 'System Logs',
- 'stats.level_distribution': 'Level Distribution',
- 'stats.all_time': 'All Time',
+ 'monitor.stats.time_range': 'Query Time Range',
+ 'monitor.stats.http_requests': 'HTTP Requests',
+ 'monitor.stats.outbound_requests': 'Outbound Requests',
+ 'monitor.stats.system_logs': 'System Logs',
+ 'monitor.stats.level_distribution': 'Level Distribution',
+ 'monitor.stats.all_time': 'All Time',
// ============ Others ============
- 'other.last_update': 'Last Update',
- 'other.auto_reconnect': 'reconnect in 5s',
- 'other.waiting_logs': 'Waiting for log data...',
- 'other.no_matching_logs': 'No matching logs',
- 'other.precise_mode': 'Precise Mode',
- 'other.saved_queries': 'Saved Queries...',
- 'other.all_time': 'All Time',
- 'other.linked_query': 'Linked Query',
- 'other.seconds': 'seconds',
- 'other.minutes': 'minutes',
- 'other.hours': 'hours',
- 'other.days': 'days',
- 'other.ms': 'ms',
- 'other.times': 'times',
- 'other.view_detail': 'View Detail',
- 'other.copy': 'Copy',
- 'other.expand': 'Expand',
- 'other.collapse': 'Collapse',
- 'other.show_internal': 'Show Internal',
- 'other.show_read': 'Show Read',
+ 'monitor.other.last_update': 'Last Update',
+ 'monitor.other.auto_reconnect': 'reconnect in 5s',
+ 'monitor.other.waiting_logs': 'Waiting for log data...',
+ 'monitor.other.no_matching_logs': 'No matching logs',
+ 'monitor.other.precise_mode': 'Precise Mode',
+ 'monitor.other.saved_queries': 'Saved Queries...',
+ 'monitor.other.all_time': 'All Time',
+ 'monitor.other.linked_query': 'Linked Query',
+ 'monitor.other.seconds': 'seconds',
+ 'monitor.other.minutes': 'minutes',
+ 'monitor.other.hours': 'hours',
+ 'monitor.other.days': 'days',
+ 'monitor.other.ms': 'ms',
+ 'monitor.other.times': 'times',
+ 'monitor.other.view_detail': 'View Detail',
+ 'monitor.other.copy': 'Copy',
+ 'monitor.other.expand': 'Expand',
+ 'monitor.other.collapse': 'Collapse',
+ 'monitor.other.show_internal': 'Show Internal',
+ 'monitor.other.show_read': 'Show Read',
// ============ Date ============
- 'date.today': 'Today',
- 'date.yesterday': 'Yesterday',
+ 'monitor.date.today': 'Today',
+ 'monitor.date.yesterday': 'Yesterday',
// ============ Language ============
- 'lang.select': 'Select Language',
- 'lang.zh': '中文',
- 'lang.en': 'English',
- 'lang.de': 'Deutsch',
+ 'monitor.lang.select': 'Select Language',
+ 'monitor.lang.zh': '中文',
+ 'monitor.lang.en': 'English',
+ 'monitor.lang.de': 'Deutsch',
// ============ HTTP Methods ============
- 'method.get': 'GET',
- 'method.post': 'POST',
- 'method.put': 'PUT',
- 'method.delete': 'DELETE',
- 'method.patch': 'PATCH',
+ 'monitor.method.get': 'GET',
+ 'monitor.method.post': 'POST',
+ 'monitor.method.put': 'PUT',
+ 'monitor.method.delete': 'DELETE',
+ 'monitor.method.patch': 'PATCH',
// ============ Log Levels ============
- 'level.debug': 'DEBUG',
- 'level.info': 'INFO',
- 'level.warning': 'WARNING',
- 'level.error': 'ERROR',
- 'level.critical': 'CRITICAL',
+ 'monitor.level.debug': 'DEBUG',
+ 'monitor.level.info': 'INFO',
+ 'monitor.level.warning': 'WARNING',
+ 'monitor.level.error': 'ERROR',
+ 'monitor.level.critical': 'CRITICAL',
// ============ Scheduler ============
- 'scheduler.rule': 'Schedule Rule',
- 'scheduler.last_run': 'Last Run',
- 'scheduler.max_time': 'Max Time',
- 'scheduler.never_run': 'Never Run',
- 'scheduler.default': 'Default',
- 'scheduler.running': 'Running',
- 'scheduler.not_scheduled': 'Not Scheduled',
+ 'monitor.scheduler.rule': 'Schedule Rule',
+ 'monitor.scheduler.last_run': 'Last Run',
+ 'monitor.scheduler.max_time': 'Max Time',
+ 'monitor.scheduler.never_run': 'Never Run',
+ 'monitor.scheduler.default': 'Default',
+ 'monitor.scheduler.running': 'Running',
+ 'monitor.scheduler.not_scheduled': 'Not Scheduled',
// ============ Relative Time ============
- 'time.just_now': 'Just now',
- 'time.minutes_ago': '{n} min ago',
- 'time.hours_ago': '{n} hours ago',
- 'time.days_ago': '{n} days ago',
- 'time.day_before_yesterday': 'Day before yesterday',
+ 'monitor.time.just_now': 'Just now',
+ 'monitor.time.minutes_ago': '{n} min ago',
+ 'monitor.time.hours_ago': '{n} hours ago',
+ 'monitor.time.days_ago': '{n} days ago',
+ 'monitor.time.day_before_yesterday': 'Day before yesterday',
// ============ Alert Types ============
- 'alert.warning': 'Warning',
- 'alert.error': 'Error',
- 'alert.critical': 'Critical',
- 'alert.normal': 'Normal',
+ 'monitor.alert.warning': 'Warning',
+ 'monitor.alert.error': 'Error',
+ 'monitor.alert.critical': 'Critical',
+ 'monitor.alert.normal': 'Normal',
// ============ Connection Status ============
- 'connection.failed': 'Connection Failed',
- 'connection.reconnecting': 'Reconnecting',
- 'connection.connected': 'Connected',
- 'connection.disconnected': 'Disconnected',
+ 'monitor.connection.failed': 'Connection Failed',
+ 'monitor.connection.reconnecting': 'Reconnecting',
+ 'monitor.connection.connected': 'Connected',
+ 'monitor.connection.disconnected': 'Disconnected',
// ============ Monitor Status ============
- 'monitor.paused': '● Monitor paused (inactive)',
- 'monitor.panel': 'Monitor Panel',
- 'monitor.last_update': 'Last Update',
- 'monitor.reset_stats_confirm': 'Reset all event statistics?',
+ 'monitor.monitor.paused': '● Monitor paused (inactive)',
+ 'monitor.monitor.panel': 'Monitor Panel',
+ 'monitor.monitor.last_update': 'Last Update',
+ 'monitor.monitor.reset_stats_confirm': 'Reset all event statistics?',
// ============ Highlight ============
- 'highlight.on': 'Highlighted',
- 'highlight.off': 'Highlight',
+ 'monitor.highlight.on': 'Highlighted',
+ 'monitor.highlight.off': 'Highlight',
// ============ Copy ============
- 'copy.success': 'Copied',
- 'copy.failed': 'Copy failed, please copy manually',
- 'copy.error': 'Copy failed',
+ 'monitor.copy.success': 'Copied',
+ 'monitor.copy.failed': 'Copy failed, please copy manually',
+ 'monitor.copy.error': 'Copy failed',
// ============ Prompts ============
- 'prompt.select_date': 'Please select a date',
- 'prompt.fetch_failed': 'Failed to fetch request records, please try again',
- 'prompt.fetch_outbound_failed': 'Failed to fetch outbound request records, please try again',
- 'prompt.reset_failed': 'Reset failed, please try again',
+ 'monitor.prompt.select_date': 'Please select a date',
+ 'monitor.prompt.fetch_failed': 'Failed to fetch request records, please try again',
+ 'monitor.prompt.fetch_outbound_failed': 'Failed to fetch outbound request records, please try again',
+ 'monitor.prompt.reset_failed': 'Reset failed, please try again',
// ============ Page Titles ============
- 'page.http_requests_log': 'HTTP Request Records',
- 'page.outbound_requests_log': 'Outbound Request Records',
+ 'monitor.page.http_requests_log': 'HTTP Request Records',
+ 'monitor.page.outbound_requests_log': 'Outbound Request Records',
// ============ Time ============
- 'time.tomorrow': 'Tomorrow',
- 'time.day_after_tomorrow': 'Day after tomorrow',
- 'time.over_24h': 'Over 24 hours',
- 'time.expired': 'Expired',
- 'time.days_hours_later': '{days}d {hours}h later',
- 'time.unknown': 'Unknown',
- 'time.month_day': '{month}/{day}',
+ 'monitor.time.tomorrow': 'Tomorrow',
+ 'monitor.time.day_after_tomorrow': 'Day after tomorrow',
+ 'monitor.time.over_24h': 'Over 24 hours',
+ 'monitor.time.expired': 'Expired',
+ 'monitor.time.days_hours_later': '{days}d {hours}h later',
+ 'monitor.time.unknown': 'Unknown',
+ 'monitor.time.month_day': '{month}/{day}',
// ============ Task Types ============
- 'task.system': 'System',
- 'task.project': 'Project',
- 'task.avg_time': 'Avg Execution Time',
+ 'monitor.task.system': 'System',
+ 'monitor.task.project': 'Project',
+ 'monitor.task.avg_time': 'Avg Execution Time',
// ============ Detail Modal ============
- 'modal.outbound_detail': 'Outbound Request Detail',
- 'modal.timestamp': 'Timestamp:',
- 'modal.method': 'Method:',
- 'modal.status_code': 'Status Code:',
- 'modal.response_time': 'Response Time:',
- 'modal.module': 'Module:',
- 'modal.error_msg': 'Error Message:',
+ 'monitor.modal.outbound_detail': 'Outbound Request Detail',
+ 'monitor.modal.timestamp': 'Timestamp:',
+ 'monitor.modal.method': 'Method:',
+ 'monitor.modal.status_code': 'Status Code:',
+ 'monitor.modal.response_time': 'Response Time:',
+ 'monitor.modal.module': 'Module:',
+ 'monitor.modal.error_msg': 'Error Message:',
// ============ Request/Response Sections ============
- 'section.request_headers': 'Request Headers',
- 'section.request_body': 'Request Body',
- 'section.response_headers': 'Response Headers',
- 'section.response_body': 'Response Body',
- 'section.no_response': 'No response body data',
- 'section.response_failed': 'Response body processing failed: ',
- 'section.truncated': '[Content truncated, export to view full content]',
- 'section.large_data_warning': '⚠️ Large data ({count} chars), may affect performance',
- 'section.showing_chars': 'Showing first {count} chars',
- 'section.basic_info': 'Basic Information',
- 'section.no_data': 'No data',
+ 'monitor.section.request_headers': 'Request Headers',
+ 'monitor.section.request_body': 'Request Body',
+ 'monitor.section.response_headers': 'Response Headers',
+ 'monitor.section.response_body': 'Response Body',
+ 'monitor.section.no_response': 'No response body data',
+ 'monitor.section.response_failed': 'Response body processing failed: ',
+ 'monitor.section.truncated': '[Content truncated, export to view full content]',
+ 'monitor.section.large_data_warning': '⚠️ Large data ({count} chars), may affect performance',
+ 'monitor.section.showing_chars': 'Showing first {count} chars',
+ 'monitor.section.basic_info': 'Basic Information',
+ 'monitor.section.no_data': 'No data',
// ============ Rate Limiting ============
- 'status.rate_limited': 'Rate Limited',
+ 'monitor.status.rate_limited': 'Rate Limited',
// ============ Log Level Selection ============
- 'log_level.all_with_debug': 'All Levels (with DEBUG)',
- 'log_level.error': 'Error Logs',
- 'log_level.warning': 'Warning Logs',
- 'log_level.info': 'Info Logs',
- 'log_level.debug': 'Debug Logs',
+ 'monitor.log_level.all_with_debug': 'All Levels (with DEBUG)',
+ 'monitor.log_level.error': 'Error Logs',
+ 'monitor.log_level.warning': 'Warning Logs',
+ 'monitor.log_level.info': 'Info Logs',
+ 'monitor.log_level.debug': 'Debug Logs',
// ============ DeadLetter ============
- 'dl.reprocess': 'Reprocess',
- 'dl.reprocess_success': 'DeadLetter reprocessed successfully',
- 'dl.reprocess_failed': 'DeadLetter reprocess failed',
+ 'monitor.dl.reprocess': 'Reprocess',
+ 'monitor.dl.reprocess_success': 'DeadLetter reprocessed successfully',
+ 'monitor.dl.reprocess_failed': 'DeadLetter reprocess failed',
// ============ Backend Alert Message Mapping ============
- 'alert.scheduler_not_running': 'Scheduler not running',
- 'alert.db_connection_failed': 'Database connection failed',
- 'alert.redis_connection_failed': 'Redis connection failed',
- 'alert.event_listener_stopped': 'Event listener stopped',
- 'alert.binlog_listener_stopped': 'Binlog listener stopped',
- 'alert.high_error_rate': 'High error rate',
- 'alert.memory_warning': 'Memory usage warning',
- 'alert.cpu_warning': 'CPU usage warning',
+ 'monitor.alert.scheduler_not_running': 'Scheduler not running',
+ 'monitor.alert.db_connection_failed': 'Database connection failed',
+ 'monitor.alert.redis_connection_failed': 'Redis connection failed',
+ 'monitor.alert.event_listener_stopped': 'Event listener stopped',
+ 'monitor.alert.binlog_listener_stopped': 'Binlog listener stopped',
+ 'monitor.alert.high_error_rate': 'High error rate',
+ 'monitor.alert.memory_warning': 'Memory usage warning',
+ 'monitor.alert.cpu_warning': 'CPU usage warning',
// ================================================================================
// MDS Module (Data Staging System)
@@ -658,5 +668,12 @@ window.__i18n_en_US__ = {
'mds.other.tip': 'Tip',
'mds.other.warning': 'Warning',
'mds.other.error': 'Error',
- 'mds.other.success': 'Success'
+ 'mds.other.success': 'Success',
+
+ // ============ 补充翻译键 ============
+ 'monitor.other.yes': 'Yes',
+ 'monitor.other.no': 'No',
+ 'monitor.other.data_points': 'data points',
+ 'monitor.col.is_slow': 'Is Slow Request',
+ 'monitor.col.is_error': 'Is Error'
};
diff --git a/static/lib/i18n/zh-CN.js b/static/lib/i18n/zh-CN.js
index 04d684e..5166d48 100644
--- a/static/lib/i18n/zh-CN.js
+++ b/static/lib/i18n/zh-CN.js
@@ -4,497 +4,507 @@
*/
window.__i18n_zh_CN__ = {
// ============ 页面标题 ============
- 'page.title': 'MyAPI 系统监控面板',
- 'page.live_logs': '实时日志 - 系统监控',
- 'page.history_logs': '日志历史查询 - 系统监控',
+ 'monitor.page.title': 'MyAPI 系统监控面板',
+ 'monitor.page.live_logs': '实时日志 - 系统监控',
+ 'monitor.page.history_logs': '日志历史查询 - 系统监控',
// ============ 导航菜单 ============
- 'nav.overview': '📊 Overview',
- 'nav.database': '🗃️ 数据库',
- 'nav.events': '☎️ 事件处理',
- 'nav.scheduler': '⏰ 定时任务',
- 'nav.http_requests': '📥 接收请求',
- 'nav.outbound_requests': '📤 发送请求',
- 'nav.logs': '📋 日志',
+ 'monitor.nav.overview': '📊 Overview',
+ 'monitor.nav.database': '🗃️ 数据库',
+ 'monitor.nav.events': '☎️ 事件处理',
+ 'monitor.nav.scheduler': '⏰ 定时任务',
+ 'monitor.nav.http_requests': '📥 接收请求',
+ 'monitor.nav.outbound_requests': '📤 发送请求',
+ 'monitor.nav.logs': '📋 日志',
// ============ 标签页 ============
- 'tab.overview': '概览',
- 'tab.database': '数据库',
- 'tab.events': '事件处理',
- 'tab.scheduler': '定时任务',
- 'tab.http': '接收请求',
- 'tab.outbound': '发送请求',
- 'tab.logs': '系统日志',
- 'tab.timeline': '时间线',
- 'tab.chart': '图表分析',
+ 'monitor.tab.overview': '概览',
+ 'monitor.tab.database': '数据库',
+ 'monitor.tab.events': '事件处理',
+ 'monitor.tab.scheduler': '定时任务',
+ 'monitor.tab.http': '接收请求',
+ 'monitor.tab.outbound': '发送请求',
+ 'monitor.tab.logs': '系统日志',
+ 'monitor.tab.timeline': '时间线',
+ 'monitor.tab.chart': '图表分析',
// ============ 卡片标题 ============
- 'card.resource': '资源使用',
- 'card.db_status': '账套状态',
- 'card.db_connections': '数据库连接状态',
- 'card.event_helpers': '事件辅助模块',
- 'card.scheduler': '定时任务调度器',
- 'card.api_requests': 'HTTP请求统计',
- 'card.outbound_requests': '对外HTTP请求',
- 'card.redis': 'Redis状态',
- 'card.event_listener': '事件监听',
- 'card.recent_alerts': '最近告警',
- 'card.mysql': 'MySQL',
- 'card.http_requests_log': '接收请求记录',
- 'card.scheduler_detail': '定时任务详情',
- 'card.outbound_requests_log': '发送请求记录',
- 'card.callback_tracker': '回调跟踪器',
- 'card.event_deduplicator': '事件去重器',
+ 'monitor.card.resource': '资源使用',
+ 'monitor.card.db_status': '账套状态',
+ 'monitor.card.db_connections': '数据库连接状态',
+ 'monitor.card.event_helpers': '事件辅助模块',
+ 'monitor.card.scheduler': '定时任务调度器',
+ 'monitor.card.api_requests': 'HTTP请求统计',
+ 'monitor.card.outbound_requests': '对外HTTP请求',
+ 'monitor.card.redis': 'Redis状态',
+ 'monitor.card.event_listener': '事件监听',
+ 'monitor.card.recent_alerts': '最近告警',
+ 'monitor.card.mysql': 'MySQL',
+ 'monitor.card.http_requests_log': '接收请求记录',
+ 'monitor.card.scheduler_detail': '定时任务详情',
+ 'monitor.card.outbound_requests_log': '发送请求记录',
+ 'monitor.card.callback_tracker': '回调跟踪器',
+ 'monitor.card.event_deduplicator': '事件去重器',
// ============ 指标标签 ============
- 'metric.cpu': 'CPU 使用率',
- 'metric.memory': '内存使用',
- 'metric.threads': '线程数',
- 'metric.uptime': '运行时间',
- 'metric.total_connections': '总连接数',
- 'metric.healthy': '健康',
- 'metric.unhealthy': '异常',
- 'metric.degraded': '降级',
- 'metric.total': '总数',
- 'metric.success': '成功',
- 'metric.failed': '失败',
- 'metric.pending': '待处理',
- 'metric.active': '活跃',
- 'metric.idle': '空闲',
- 'metric.unknown': '未知',
- 'metric.warning': '警告',
- 'metric.events_received': '接收事件',
- 'metric.events_processed': '处理事件',
- 'metric.events_failed': '失败事件',
- 'metric.jobs_running': '运行中任务',
- 'metric.jobs_pending': '待执行任务',
- 'metric.requests_total': '总请求数',
- 'metric.requests_slow': '慢请求',
- 'metric.requests_error': '错误请求',
- 'metric.error_rate': '错误率',
- 'metric.avg_time': '平均响应',
- 'metric.connection_status': '连接状态',
- 'metric.host': '主机',
- 'metric.port': '端口',
- 'metric.database': '数据库',
- 'metric.used_connections': '使用连接数',
- 'metric.max_connections': '最大连接数',
- 'metric.connection_usage': '连接使用率',
- 'metric.buffer_size': '缓冲大小',
- 'metric.buffer_threshold': '缓冲阈值',
- 'metric.buffer_usage': '缓冲使用率',
- 'metric.events_interrupted': '中断处理',
- 'metric.overall_success_rate': '整体成功率',
- 'metric.active_event_types': '活跃事件类型',
- 'metric.backpressure_status': '背压状态',
- 'metric.backpressure_pending': '待处理事件',
- 'metric.backpressure_usage': '背压使用率',
- 'metric.event_loop_status': '事件循环状态',
- 'metric.pending_callbacks': '待处理回调',
- 'metric.max_retries': '最大重试次数',
- 'metric.pending_retries': '待处理重试',
- 'metric.total_entries': '总存储条目',
- 'metric.active_items': '活跃项目',
- 'metric.ttl_seconds': 'TTL (秒)',
- 'metric.max_entries': '最大条目',
- 'metric.dl_total': 'DL总数',
- 'metric.recent_dl': '最近DL',
- 'metric.process_success_rate': '处理成功率',
- 'metric.scheduler_status': '调度器状态',
- 'metric.job_count': '任务数量',
- 'metric.pending_events': '待写入事件',
- 'metric.total_events': '总事件数',
- 'metric.file_size': '文件大小',
- 'metric.running_status': '运行状态',
+ 'monitor.metric.cpu': 'CPU 使用率',
+ 'monitor.metric.memory': '内存使用',
+ 'monitor.metric.threads': '线程数',
+ 'monitor.metric.uptime': '运行时间',
+ 'monitor.metric.total_connections': '总连接数',
+ 'monitor.metric.healthy': '健康',
+ 'monitor.metric.unhealthy': '异常',
+ 'monitor.metric.degraded': '降级',
+ 'monitor.metric.total': '总数',
+ 'monitor.metric.success': '成功',
+ 'monitor.metric.failed': '失败',
+ 'monitor.metric.pending': '待处理',
+ 'monitor.metric.active': '活跃',
+ 'monitor.metric.idle': '空闲',
+ 'monitor.metric.unknown': '未知',
+ 'monitor.metric.warning': '警告',
+ 'monitor.metric.events_received': '接收事件',
+ 'monitor.metric.events_processed': '处理事件',
+ 'monitor.metric.events_failed': '失败事件',
+ 'monitor.metric.jobs_running': '运行中任务',
+ 'monitor.metric.jobs_pending': '待执行任务',
+ 'monitor.metric.requests_total': '总请求数',
+ 'monitor.metric.requests_slow': '慢请求',
+ 'monitor.metric.requests_error': '错误请求',
+ 'monitor.metric.error_rate': '错误率',
+ 'monitor.metric.avg_time': '平均响应',
+ 'monitor.metric.connection_status': '连接状态',
+ 'monitor.metric.host': '主机',
+ 'monitor.metric.port': '端口',
+ 'monitor.metric.database': '数据库',
+ 'monitor.metric.used_connections': '使用连接数',
+ 'monitor.metric.max_connections': '最大连接数',
+ 'monitor.metric.connection_usage': '连接使用率',
+ 'monitor.metric.buffer_size': '缓冲大小',
+ 'monitor.metric.buffer_threshold': '缓冲阈值',
+ 'monitor.metric.buffer_usage': '缓冲使用率',
+ 'monitor.metric.events_interrupted': '中断处理',
+ 'monitor.metric.overall_success_rate': '整体成功率',
+ 'monitor.metric.active_event_types': '活跃事件类型',
+ 'monitor.metric.backpressure_status': '背压状态',
+ 'monitor.metric.backpressure_pending': '待处理事件',
+ 'monitor.metric.backpressure_usage': '背压使用率',
+ 'monitor.metric.event_loop_status': '事件循环状态',
+ 'monitor.metric.pending_callbacks': '待处理回调',
+ 'monitor.metric.max_retries': '最大重试次数',
+ 'monitor.metric.pending_retries': '待处理重试',
+ 'monitor.metric.total_entries': '总存储条目',
+ 'monitor.metric.active_items': '活跃项目',
+ 'monitor.metric.ttl_seconds': 'TTL (秒)',
+ 'monitor.metric.max_entries': '最大条目',
+ 'monitor.metric.dl_total': 'DL总数',
+ 'monitor.metric.recent_dl': '最近DL',
+ 'monitor.metric.process_success_rate': '处理成功率',
+ 'monitor.metric.scheduler_status': '调度器状态',
+ 'monitor.metric.job_count': '任务数量',
+ 'monitor.metric.pending_events': '待写入事件',
+ 'monitor.metric.total_events': '总事件数',
+ 'monitor.metric.file_size': '文件大小',
+ 'monitor.metric.running_status': '运行状态',
// ============ 状态 ============
- 'status.healthy': '● 系统正常',
- 'status.unhealthy': '● 系统异常',
- 'status.degraded': '● 系统降级',
- 'status.running': '运行中',
- 'status.checking': '检查中',
- 'status.stopped': '已停止',
- 'status.loading': '加载中...',
- 'status.querying': '查询中...',
- 'status.exporting': '导出中...',
- 'status.no_data': '暂无数据',
- 'status.connected': '已连接',
- 'status.connecting': '连接中...',
- 'status.disconnected': '已断开',
- 'status.reconnecting': '重连中...',
- 'status.paused': '已暂停',
- 'status.enabled': '已启用',
- 'status.disabled': '已禁用',
- 'status.no_db_connections': '暂无数据库连接',
- 'status.no_scheduler': '暂无定时任务',
- 'status.no_http_requests': '暂无接收请求',
- 'status.no_alerts': '暂无告警',
- 'status.no_logs': '暂无日志',
- 'status.no_events': '暂无事件统计',
- 'status.no_api_requests': '暂无 API 请求记录',
- 'status.no_outbound_requests': '暂无发送请求记录',
- 'status.no_dead_letters': '暂无DL',
- 'status.partial_warnings': '● 部分警告',
- 'status.monitoring': '监控中',
+ 'monitor.status.healthy': '● 系统正常',
+ 'monitor.status.unhealthy': '● 系统异常',
+ 'monitor.status.degraded': '● 系统降级',
+ 'monitor.status.running': '运行中',
+ 'monitor.status.checking': '检查中',
+ 'monitor.status.stopped': '已停止',
+ 'monitor.status.loading': '加载中...',
+ 'monitor.status.querying': '查询中...',
+ 'monitor.status.exporting': '导出中...',
+ 'monitor.status.no_data': '暂无数据',
+ 'monitor.status.connected': '已连接',
+ 'monitor.status.connecting': '连接中...',
+ 'monitor.status.disconnected': '已断开',
+ 'monitor.status.reconnecting': '重连中...',
+ 'monitor.status.paused': '已暂停',
+ 'monitor.status.enabled': '已启用',
+ 'monitor.status.disabled': '已禁用',
+ 'monitor.status.no_db_connections': '暂无数据库连接',
+ 'monitor.status.no_scheduler': '暂无定时任务',
+ 'monitor.status.no_http_requests': '暂无接收请求',
+ 'monitor.status.no_alerts': '暂无告警',
+ 'monitor.status.no_logs': '暂无日志',
+ 'monitor.status.no_events': '暂无事件统计',
+ 'monitor.status.no_api_requests': '暂无 API 请求记录',
+ 'monitor.status.no_outbound_requests': '暂无发送请求记录',
+ 'monitor.status.no_dead_letters': '暂无DL',
+ 'monitor.status.partial_warnings': '● 部分警告',
+ 'monitor.status.monitoring': '监控中',
// ============ 按钮 ============
- 'btn.query': '查询',
- 'btn.reset': '重置',
- 'btn.refresh': '刷新',
- 'btn.export': '导出',
- 'btn.export_csv': '导出 CSV',
- 'btn.export_json': '导出 JSON',
- 'btn.detail': '详情',
- 'btn.pause': '暂停',
- 'btn.resume': '继续',
- 'btn.clear': '清空',
- 'btn.save': '保存',
- 'btn.delete': '删除',
- 'btn.close': '关闭',
- 'btn.confirm': '确认',
- 'btn.cancel': '取消',
- 'btn.test': '测试',
- 'btn.apply': '应用',
- 'btn.filter': '过滤',
- 'btn.clear_all': '清空全部',
- 'btn.realtime_on': '实时追踪: 开',
- 'btn.realtime_off': '实时追踪: 关',
- 'btn.precise_locate': '± 60s',
- 'btn.precise_cancel': '± 60s - 点击取消',
- 'btn.reset_stats': '重置统计',
- 'btn.flush_all': '立即刷新所有',
- 'btn.new_window': '新窗口',
- 'btn.auto_scroll': '自动滚动',
- 'btn.live_logs': '实时日志',
- 'btn.history_query': '历史查询',
- 'btn.mark_all_read': '标记全部已读',
- 'btn.clear_read_status': '清空已读状态',
+ 'monitor.btn.query': '查询',
+ 'monitor.btn.reset': '重置',
+ 'monitor.btn.refresh': '刷新',
+ 'monitor.btn.export': '导出',
+ 'monitor.btn.export_csv': '导出 CSV',
+ 'monitor.btn.export_json': '导出 JSON',
+ 'monitor.btn.detail': '详情',
+ 'monitor.btn.pause': '暂停',
+ 'monitor.btn.resume': '继续',
+ 'monitor.btn.clear': '清空',
+ 'monitor.btn.save': '保存',
+ 'monitor.btn.delete': '删除',
+ 'monitor.btn.close': '关闭',
+ 'monitor.btn.confirm': '确认',
+ 'monitor.btn.cancel': '取消',
+ 'monitor.btn.test': '测试',
+ 'monitor.btn.apply': '应用',
+ 'monitor.btn.filter': '过滤',
+ 'monitor.btn.clear_all': '清空全部',
+ 'monitor.btn.realtime_on': '实时追踪: 开',
+ 'monitor.btn.realtime_off': '实时追踪: 关',
+ 'monitor.btn.precise_locate': '± 60s',
+ 'monitor.btn.precise_cancel': '± 60s - 点击取消',
+ 'monitor.btn.reset_stats': '重置统计',
+ 'monitor.btn.flush_all': '立即刷新所有',
+ 'monitor.btn.new_window': '新窗口',
+ 'monitor.btn.auto_scroll': '自动滚动',
+ 'monitor.btn.live_logs': '实时日志',
+ 'monitor.btn.history_query': '历史查询',
+ 'monitor.btn.mark_all_read': '标记全部已读',
+ 'monitor.btn.clear_read_status': '清空已读状态',
// ============ 表格列名 ============
- 'col.index': '序号',
- 'col.time': '时间',
- 'col.level': '级别',
- 'col.module': '模块',
- 'col.message': '消息',
- 'col.method': '方法',
- 'col.path': '端点',
- 'col.url': 'URL',
- 'col.status': '状态码',
- 'col.duration': '响应时间',
- 'col.client_ip': '客户端IP',
- 'col.source': '来源',
- 'col.function': '函数',
- 'col.line': '行号',
- 'col.operation': '操作',
- 'col.count': '数量',
- 'col.avg_time': '平均时间',
- 'col.max_time': '最大时间',
- 'col.db_name': '账套名称',
- 'col.last_check': '最后检查',
- 'col.current_connections': '当前连接',
- 'col.max_connections': '最大连接',
- 'col.min_connections': '最小连接',
- 'col.idle_connections': '空闲连接',
- 'col.used_connections': '使用中连接',
- 'col.usage': '使用率',
- 'col.processed_records': '处理记录',
- 'col.timestamp': '时间戳',
- 'col.query_params': '查询参数',
- 'col.error_message': '错误信息',
- 'col.url': 'URL',
- 'col.is_read': '已读',
- 'col.description': '描述',
- 'col.total_received': '总接收',
- 'col.pending': '待处理',
- 'col.processed': '已处理',
- 'col.interrupted': '中断',
- 'col.completion_rate': '完成率',
- 'col.avg_latency': '平均延迟',
- 'col.last_action': '最后动作',
- 'col.id': 'ID',
- 'col.event_type': '事件类型',
- 'col.table': '表',
+ 'monitor.col.index': '序号',
+ 'monitor.col.time': '时间',
+ 'monitor.col.level': '级别',
+ 'monitor.col.module': '模块',
+ 'monitor.col.message': '消息',
+ 'monitor.col.method': '方法',
+ 'monitor.col.path': '端点',
+ 'monitor.col.url': 'URL',
+ 'monitor.col.status': '状态码',
+ 'monitor.col.duration': '响应时间',
+ 'monitor.col.client_ip': '客户端IP',
+ 'monitor.col.source': '来源',
+ 'monitor.col.function': '函数',
+ 'monitor.col.line': '行号',
+ 'monitor.col.operation': '操作',
+ 'monitor.col.count': '数量',
+ 'monitor.col.avg_time': '平均时间',
+ 'monitor.col.max_time': '最大时间',
+ 'monitor.col.db_name': '账套名称',
+ 'monitor.col.last_check': '最后检查',
+ 'monitor.col.current_connections': '当前连接',
+ 'monitor.col.max_connections': '最大连接',
+ 'monitor.col.min_connections': '最小连接',
+ 'monitor.col.idle_connections': '空闲连接',
+ 'monitor.col.used_connections': '使用中连接',
+ 'monitor.col.usage': '使用率',
+ 'monitor.col.processed_records': '处理记录',
+ 'monitor.col.timestamp': '时间戳',
+ 'monitor.col.query_params': '查询参数',
+ 'monitor.col.error_message': '错误信息',
+ 'monitor.col.url': 'URL',
+ 'monitor.col.is_read': '已读',
+ 'monitor.col.description': '描述',
+ 'monitor.col.total_received': '总接收',
+ 'monitor.col.pending': '待处理',
+ 'monitor.col.processed': '已处理',
+ 'monitor.col.interrupted': '中断',
+ 'monitor.col.completion_rate': '完成率',
+ 'monitor.col.avg_latency': '平均延迟',
+ 'monitor.col.last_action': '最后动作',
+ 'monitor.col.id': 'ID',
+ 'monitor.col.event_type': '事件类型',
+ 'monitor.col.table': '表',
// ============ 快捷时间 ============
- 'time.start_time': '开始时间',
- 'time.end_time': '结束时间',
- 'time.last_10m': '最近10分钟',
- 'time.last_30m': '最近30分钟',
- 'time.last_1h': '最近1小时',
- 'time.last_6h': '最近6小时',
- 'time.last_24h': '最近24小时',
- 'time.range': '时间范围',
+ 'monitor.time.start_time': '开始时间',
+ 'monitor.time.end_time': '结束时间',
+ 'monitor.time.last_10m': '最近10分钟',
+ 'monitor.time.last_30m': '最近30分钟',
+ 'monitor.time.last_1h': '最近1小时',
+ 'monitor.time.last_6h': '最近6小时',
+ 'monitor.time.last_24h': '最近24小时',
+ 'monitor.time.range': '时间范围',
// ============ 过滤条件 ============
- 'filter.level': '全部级别',
- 'filter.type': '全部数据',
- 'filter.module': '模块',
- 'filter.keyword': '关键词',
- 'filter.method': '请求方法',
- 'filter.client_ip': '客户端IP',
- 'filter.status_range': '状态码范围',
- 'filter.duration_range': '响应时间范围',
- 'filter.advanced': '高级过滤',
- 'filter.collapse': '收起',
- 'filter.expand': '展开',
- 'filter.clear': '清空',
- 'filter.active': '已激活过滤条件',
- 'filter.module_placeholder': '输入模块名',
- 'filter.keyword_placeholder': '输入关键词搜索',
- 'filter.ip_placeholder': '输入客户端IP',
- 'filter.status_placeholder': '如: 400-499',
- 'filter.duration_placeholder': '如: >1000',
- 'filter.all_logs': '全部日志',
+ 'monitor.filter.level': '全部级别',
+ 'monitor.filter.type': '全部数据',
+ 'monitor.filter.module': '模块',
+ 'monitor.filter.keyword': '关键词',
+ 'monitor.filter.method': '请求方法',
+ 'monitor.filter.client_ip': '客户端IP',
+ 'monitor.filter.status_range': '状态码范围',
+ 'monitor.filter.duration_range': '响应时间范围',
+ 'monitor.filter.advanced': '高级过滤',
+ 'monitor.filter.collapse': '收起',
+ 'monitor.filter.expand': '展开',
+ 'monitor.filter.clear': '清空',
+ 'monitor.filter.active': '已激活过滤条件',
+ 'monitor.filter.module_placeholder': '输入模块名',
+ 'monitor.filter.keyword_placeholder': '输入关键词搜索',
+ 'monitor.filter.ip_placeholder': '输入客户端IP',
+ 'monitor.filter.status_placeholder': '如: 400-499',
+ 'monitor.filter.duration_placeholder': '如: >1000',
+ 'monitor.filter.all_logs': '全部日志',
// ============ 图表 ============
- 'chart.request_trend': '📊 请求量趋势',
- 'chart.level_distribution': '📊 日志级别分布',
- 'chart.status_distribution': '📈 状态码分布',
- 'chart.slow_requests': '⏱️ 慢请求TOP10',
- 'chart.total_requests': '总请求',
- 'chart.error_count': '错误',
- 'chart.slow_count': '慢请求',
- 'chart.log_count': '日志总数',
- 'chart.no_slow': '✅ 无慢请求',
- 'chart.query_required': '请先执行查询',
- 'chart.load_failed': '❌ 图表数据加载失败,请重试',
- 'chart.redis_connections': 'Redis 连接池使用情况',
- 'chart.redis_buffer': 'Redis 缓冲大小变化',
- 'chart.cpu': 'CPU',
- 'chart.memory': '内存',
- 'chart.upload': '上传',
- 'chart.download': '下载',
- 'chart.cpu_memory_axis': 'CPU / 内存 (%)',
- 'chart.network_axis': '网络上传 / 下载 (KB/s)',
- 'chart.used_connections': '使用连接数',
- 'chart.buffer_size_mb': '缓冲大小 (MB)',
+ 'monitor.chart.request_trend': '📊 请求量趋势',
+ 'monitor.chart.level_distribution': '📊 日志级别分布',
+ 'monitor.chart.status_distribution': '📈 状态码分布',
+ 'monitor.chart.slow_requests': '⏱️ 慢请求TOP10',
+ 'monitor.chart.total_requests': '总请求',
+ 'monitor.chart.error_count': '错误',
+ 'monitor.chart.slow_count': '慢请求',
+ 'monitor.chart.log_count': '日志总数',
+ 'monitor.chart.no_slow': '✅ 无慢请求',
+ 'monitor.chart.query_required': '请先执行查询',
+ 'monitor.chart.load_failed': '❌ 图表数据加载失败,请重试',
+ 'monitor.chart.no_data': '无数据',
+ 'monitor.chart.no_trend': '无趋势数据',
+ 'monitor.chart.redis_connections': 'Redis 连接池使用情况',
+ 'monitor.chart.redis_buffer': 'Redis 缓冲大小变化',
+ 'monitor.chart.cpu': 'CPU',
+ 'monitor.chart.memory': '内存',
+ 'monitor.chart.upload': '上传',
+ 'monitor.chart.download': '下载',
+ 'monitor.chart.cpu_memory_axis': 'CPU / 内存 (%)',
+ 'monitor.chart.network_axis': '网络上传 / 下载 (KB/s)',
+ 'monitor.chart.used_connections': '使用连接数',
+ 'monitor.chart.buffer_size_mb': '缓冲大小 (MB)',
// ============ 错误提示 ============
- 'error.time_range_invalid': '开始时间不能大于结束时间',
- 'error.time_range_required': '请选择开始时间和结束时间',
- 'error.query_failed': '查询失败,请稍后重试',
- 'error.export_failed': '导出失败,请重试',
- 'error.connection_failed': '连接失败,请刷新页面',
- 'error.max_templates': '最多保存10个模板,请先删除部分模板',
- 'error.auto_pause': '实时追踪已自动暂停(超过10分钟)',
- 'error.load_failed': '加载失败,请重试',
- 'error.invalid_params': '参数错误',
- 'error.clear_dl_failed': '清空DeadLetter队列失败',
+ 'monitor.error.time_range_invalid': '开始时间不能大于结束时间',
+ 'monitor.error.time_range_required': '请选择开始时间和结束时间',
+ 'monitor.error.query_failed': '查询失败,请稍后重试',
+ 'monitor.error.export_failed': '导出失败,请重试',
+ 'monitor.error.connection_failed': '连接失败,请刷新页面',
+ 'monitor.error.max_templates': '最多保存10个模板,请先删除部分模板',
+ 'monitor.error.auto_pause': '实时追踪已自动暂停(超过10分钟)',
+ 'monitor.error.load_failed': '加载失败,请重试',
+ 'monitor.error.invalid_params': '参数错误',
+ 'monitor.error.clear_dl_failed': '清空DeadLetter队列失败',
// ============ 成功提示 ============
- 'success.query_complete': '查询完成',
- 'success.export_complete': '导出完成',
- 'success.template_saved': '模板"{name}"已保存',
- 'success.logs_cleared': '日志已清空',
- 'success.operation_success': '操作成功',
- 'success.dl_cleared': 'DeadLetter队列已清空',
+ 'monitor.success.query_complete': '查询完成',
+ 'monitor.success.export_complete': '导出完成',
+ 'monitor.success.template_saved': '模板"{name}"已保存',
+ 'monitor.success.logs_cleared': '日志已清空',
+ 'monitor.success.operation_success': '操作成功',
+ 'monitor.success.dl_cleared': 'DeadLetter队列已清空',
// ============ 时间线 ============
- 'timeline.title': '时间线',
- 'timeline.no_data': '暂无数据,请先执行查询',
- 'timeline.anomaly_detected': '⚠️ 发现 {count} 处异常',
- 'timeline.error_burst': '连续{count}条ERROR日志',
- 'timeline.slow_anomaly': '异常慢请求({duration}ms > 平均{avg}ms×3)',
- 'timeline.duplicate_error': '重复错误"{msg}"出现{count}次',
- 'timeline.http_request': 'HTTP请求',
- 'timeline.outbound_request': '发送请求',
- 'timeline.system_log': '系统日志',
+ 'monitor.timeline.title': '时间线',
+ 'monitor.timeline.no_data': '暂无数据,请先执行查询',
+ 'monitor.timeline.anomaly_detected': '⚠️ 发现 {count} 处异常',
+ 'monitor.timeline.error_burst': '连续{count}条ERROR日志',
+ 'monitor.timeline.slow_anomaly': '异常慢请求({duration}ms > 平均{avg}ms×3)',
+ 'monitor.timeline.duplicate_error': '重复错误"{msg}"出现{count}次',
+ 'monitor.timeline.http_request': 'HTTP请求',
+ 'monitor.timeline.outbound_request': '发送请求',
+ 'monitor.timeline.system_log': '系统日志',
+ 'monitor.timeline.summary_title': '📊 时间线统计',
+ 'monitor.timeline.total_events': '总事件',
+ 'monitor.timeline.http_requests': 'HTTP请求',
+ 'monitor.timeline.outbound_requests': '发送请求',
+ 'monitor.timeline.system_logs': '系统日志',
+ 'monitor.timeline.errors': '错误',
+ 'monitor.timeline.warnings': '警告',
+ 'monitor.timeline.slow_requests': '慢请求',
// ============ 分页 ============
- 'pagination.page': '页',
- 'pagination.of': '共',
- 'pagination.items': '条',
- 'pagination.showing': '显示',
- 'pagination.per_page': '每页',
- 'pagination.first': '首页',
- 'pagination.last': '末页',
- 'pagination.prev': '上一页',
- 'pagination.next': '下一页',
- 'pagination.go_to': '跳转',
+ 'monitor.pagination.page': '页',
+ 'monitor.pagination.of': '共',
+ 'monitor.pagination.items': '条',
+ 'monitor.pagination.showing': '显示',
+ 'monitor.pagination.per_page': '每页',
+ 'monitor.pagination.first': '首页',
+ 'monitor.pagination.last': '末页',
+ 'monitor.pagination.prev': '上一页',
+ 'monitor.pagination.next': '下一页',
+ 'monitor.pagination.go_to': '跳转',
// ============ 导出 ============
- 'export.current_page': '导出当前页',
- 'export.all_data': '导出全部数据',
- 'export.format_csv': 'CSV格式',
- 'export.format_json': 'JSON格式',
- 'export.select_format': '选择导出格式',
+ 'monitor.export.current_page': '导出当前页',
+ 'monitor.export.all_data': '导出全部数据',
+ 'monitor.export.format_csv': 'CSV格式',
+ 'monitor.export.format_json': 'JSON格式',
+ 'monitor.export.select_format': '选择导出格式',
// ============ 查询模板 ============
- 'template.saved_queries': '已保存查询...',
- 'template.save': '保存查询条件',
- 'template.manage': '管理模板',
- 'template.name': '模板名称',
- 'template.description': '描述',
- 'template.delete_confirm': '确定删除该模板?',
- 'template.empty': '暂无保存的模板',
+ 'monitor.template.saved_queries': '已保存查询...',
+ 'monitor.template.save': '保存查询条件',
+ 'monitor.template.manage': '管理模板',
+ 'monitor.template.name': '模板名称',
+ 'monitor.template.description': '描述',
+ 'monitor.template.delete_confirm': '确定删除该模板?',
+ 'monitor.template.empty': '暂无保存的模板',
// ============ 统计信息 ============
- 'stats.time_range': '查询时间范围',
- 'stats.http_requests': '接收请求',
- 'stats.outbound_requests': '发送请求',
- 'stats.system_logs': '系统日志',
- 'stats.level_distribution': '级别分布',
- 'stats.all_time': '全部时间',
+ 'monitor.stats.time_range': '查询时间范围',
+ 'monitor.stats.http_requests': '接收请求',
+ 'monitor.stats.outbound_requests': '发送请求',
+ 'monitor.stats.system_logs': '系统日志',
+ 'monitor.stats.level_distribution': '级别分布',
+ 'monitor.stats.all_time': '全部时间',
// ============ 其他 ============
- 'other.last_update': '最后更新',
- 'other.auto_reconnect': '5秒后重连',
- 'other.waiting_logs': '正在等待日志数据...',
- 'other.no_matching_logs': '没有匹配的日志',
- 'other.precise_mode': '精确定位',
- 'other.saved_queries': '已保存查询...',
- 'other.all_time': '全部时间',
- 'other.linked_query': '联动查询',
- 'other.seconds': '秒',
- 'other.minutes': '分钟',
- 'other.hours': '小时',
- 'other.days': '天',
- 'other.ms': '毫秒',
- 'other.times': '次',
- 'other.view_detail': '查看详情',
- 'other.copy': '复制',
- 'other.expand': '展开',
- 'other.collapse': '收起',
- 'other.show_internal': '显示内部请求',
- 'other.show_read': '显示已读',
+ 'monitor.other.last_update': '最后更新',
+ 'monitor.other.auto_reconnect': '5秒后重连',
+ 'monitor.other.waiting_logs': '正在等待日志数据...',
+ 'monitor.other.no_matching_logs': '没有匹配的日志',
+ 'monitor.other.precise_mode': '精确定位',
+ 'monitor.other.saved_queries': '已保存查询...',
+ 'monitor.other.all_time': '全部时间',
+ 'monitor.other.linked_query': '联动查询',
+ 'monitor.other.seconds': '秒',
+ 'monitor.other.minutes': '分钟',
+ 'monitor.other.hours': '小时',
+ 'monitor.other.days': '天',
+ 'monitor.other.ms': '毫秒',
+ 'monitor.other.times': '次',
+ 'monitor.other.view_detail': '查看详情',
+ 'monitor.other.copy': '复制',
+ 'monitor.other.expand': '展开',
+ 'monitor.other.collapse': '收起',
+ 'monitor.other.show_internal': '显示内部请求',
+ 'monitor.other.show_read': '显示已读',
// ============ 日期 ============
- 'date.today': '今天',
- 'date.yesterday': '昨天',
+ 'monitor.date.today': '今天',
+ 'monitor.date.yesterday': '昨天',
// ============ 语言选择 ============
- 'lang.select': '选择语言',
- 'lang.zh': '中文',
- 'lang.en': 'English',
- 'lang.de': 'Deutsch',
+ 'monitor.lang.select': '选择语言',
+ 'monitor.lang.zh': '中文',
+ 'monitor.lang.en': 'English',
+ 'monitor.lang.de': 'Deutsch',
// ============ HTTP方法 ============
- 'method.get': 'GET',
- 'method.post': 'POST',
- 'method.put': 'PUT',
- 'method.delete': 'DELETE',
- 'method.patch': 'PATCH',
+ 'monitor.method.get': 'GET',
+ 'monitor.method.post': 'POST',
+ 'monitor.method.put': 'PUT',
+ 'monitor.method.delete': 'DELETE',
+ 'monitor.method.patch': 'PATCH',
// ============ 日志级别 ============
- 'level.debug': 'DEBUG',
- 'level.info': 'INFO',
- 'level.warning': 'WARNING',
- 'level.error': 'ERROR',
- 'level.critical': 'CRITICAL',
+ 'monitor.level.debug': 'DEBUG',
+ 'monitor.level.info': 'INFO',
+ 'monitor.level.warning': 'WARNING',
+ 'monitor.level.error': 'ERROR',
+ 'monitor.level.critical': 'CRITICAL',
// ============ 定时任务 ============
- 'scheduler.rule': '定时规则',
- 'scheduler.last_run': '最近执行',
- 'scheduler.max_time': '最大执行时间',
- 'scheduler.never_run': '从未执行',
- 'scheduler.default': '默认',
- 'scheduler.running': '执行中',
- 'scheduler.not_scheduled': '未计划',
+ 'monitor.scheduler.rule': '定时规则',
+ 'monitor.scheduler.last_run': '最近执行',
+ 'monitor.scheduler.max_time': '最大执行时间',
+ 'monitor.scheduler.never_run': '从未执行',
+ 'monitor.scheduler.default': '默认',
+ 'monitor.scheduler.running': '执行中',
+ 'monitor.scheduler.not_scheduled': '未计划',
// ============ 相对时间 ============
- 'time.just_now': '刚刚',
- 'time.minutes_ago': '{n}分钟前',
- 'time.hours_ago': '{n}小时前',
- 'time.days_ago': '{n}天前',
- 'time.day_before_yesterday': '前天',
+ 'monitor.time.just_now': '刚刚',
+ 'monitor.time.minutes_ago': '{n}分钟前',
+ 'monitor.time.hours_ago': '{n}小时前',
+ 'monitor.time.days_ago': '{n}天前',
+ 'monitor.time.day_before_yesterday': '前天',
// ============ 告警类型 ============
- 'alert.warning': '警告',
- 'alert.error': '错误',
- 'alert.critical': '严重',
- 'alert.normal': '正常',
+ 'monitor.alert.warning': '警告',
+ 'monitor.alert.error': '错误',
+ 'monitor.alert.critical': '严重',
+ 'monitor.alert.normal': '正常',
// ============ 连接状态 ============
- 'connection.failed': '连接失败',
- 'connection.reconnecting': '重新连接',
- 'connection.connected': '已连接',
- 'connection.disconnected': '断开',
+ 'monitor.connection.failed': '连接失败',
+ 'monitor.connection.reconnecting': '重新连接',
+ 'monitor.connection.connected': '已连接',
+ 'monitor.connection.disconnected': '断开',
// ============ 监控状态 ============
- 'monitor.paused': '● 监控已暂停(长时间未活动)',
- 'monitor.panel': '监控面板',
- 'monitor.last_update': '最后更新',
- 'monitor.reset_stats_confirm': '确定要重置所有事件统计吗?',
+ 'monitor.monitor.paused': '● 监控已暂停(长时间未活动)',
+ 'monitor.monitor.panel': '监控面板',
+ 'monitor.monitor.last_update': '最后更新',
+ 'monitor.monitor.reset_stats_confirm': '确定要重置所有事件统计吗?',
// ============ 高亮操作 ============
- 'highlight.on': '已高亮',
- 'highlight.off': '高亮',
+ 'monitor.highlight.on': '已高亮',
+ 'monitor.highlight.off': '高亮',
// ============ 复制操作 ============
- 'copy.success': '已复制',
- 'copy.failed': '复制失败,请手动复制',
- 'copy.error': '复制失败',
+ 'monitor.copy.success': '已复制',
+ 'monitor.copy.failed': '复制失败,请手动复制',
+ 'monitor.copy.error': '复制失败',
// ============ 选择提示 ============
- 'prompt.select_date': '请选择日期',
- 'prompt.fetch_failed': '获取请求记录失败,请稍后重试',
- 'prompt.fetch_outbound_failed': '获取对外请求记录失败,请稍后重试',
- 'prompt.reset_failed': '重置失败,请重试',
+ 'monitor.prompt.select_date': '请选择日期',
+ 'monitor.prompt.fetch_failed': '获取请求记录失败,请稍后重试',
+ 'monitor.prompt.fetch_outbound_failed': '获取对外请求记录失败,请稍后重试',
+ 'monitor.prompt.reset_failed': '重置失败,请重试',
// ============ 页面标题 ============
- 'page.http_requests_log': '接收请求记录',
- 'page.outbound_requests_log': '发送请求记录',
+ 'monitor.page.http_requests_log': '接收请求记录',
+ 'monitor.page.outbound_requests_log': '发送请求记录',
// ============ 时间相关 ============
- 'time.tomorrow': '明天',
- 'time.day_after_tomorrow': '后天',
- 'time.over_24h': '超过24小时',
- 'time.expired': '已过期',
- 'time.days_hours_later': '{days}天{hours}小时后',
- 'time.unknown': '未知',
- 'time.month_day': '{month}月{day}日',
+ 'monitor.time.tomorrow': '明天',
+ 'monitor.time.day_after_tomorrow': '后天',
+ 'monitor.time.over_24h': '超过24小时',
+ 'monitor.time.expired': '已过期',
+ 'monitor.time.days_hours_later': '{days}天{hours}小时后',
+ 'monitor.time.unknown': '未知',
+ 'monitor.time.month_day': '{month}月{day}日',
// ============ 任务类型 ============
- 'task.system': '系统',
- 'task.project': '项目',
- 'task.avg_time': '平均执行时间',
+ 'monitor.task.system': '系统',
+ 'monitor.task.project': '项目',
+ 'monitor.task.avg_time': '平均执行时间',
// ============ 详情模态框 ============
- 'modal.outbound_detail': '发送请求详情',
- 'modal.timestamp': '时间戳:',
- 'modal.method': '方法:',
- 'modal.status_code': '状态码:',
- 'modal.response_time': '响应时间:',
- 'modal.module': '模块:',
- 'modal.error_msg': '错误信息:',
+ 'monitor.modal.outbound_detail': '发送请求详情',
+ 'monitor.modal.timestamp': '时间戳:',
+ 'monitor.modal.method': '方法:',
+ 'monitor.modal.status_code': '状态码:',
+ 'monitor.modal.response_time': '响应时间:',
+ 'monitor.modal.module': '模块:',
+ 'monitor.modal.error_msg': '错误信息:',
// ============ 请求/响应部分 ============
- 'section.request_headers': '请求头',
- 'section.request_body': '请求体',
- 'section.response_headers': '响应头',
- 'section.response_body': '响应体',
- 'section.no_response': '无响应体数据',
- 'section.response_failed': '响应体处理失败: ',
- 'section.truncated': '[内容已截断,完整内容请导出查看]',
- 'section.large_data_warning': '⚠️ 数据量较大({count}字符),可能影响显示性能',
- 'section.showing_chars': '仅显示前{count}字符',
- 'section.basic_info': '基本信息',
- 'section.no_data': '无数据',
+ 'monitor.section.request_headers': '请求头',
+ 'monitor.section.request_body': '请求体',
+ 'monitor.section.response_headers': '响应头',
+ 'monitor.section.response_body': '响应体',
+ 'monitor.section.no_response': '无响应体数据',
+ 'monitor.section.response_failed': '响应体处理失败: ',
+ 'monitor.section.truncated': '[内容已截断,完整内容请导出查看]',
+ 'monitor.section.large_data_warning': '⚠️ 数据量较大({count}字符),可能影响显示性能',
+ 'monitor.section.showing_chars': '仅显示前{count}字符',
+ 'monitor.section.basic_info': '基本信息',
+ 'monitor.section.no_data': '无数据',
// ============ 限流状态 ============
- 'status.rate_limited': '限流',
+ 'monitor.status.rate_limited': '限流',
// ============ 日志级别选择 ============
- 'log_level.all_with_debug': '全部级别 (含DEBUG)',
- 'log_level.error': '错误日志',
- 'log_level.warning': '警告日志',
- 'log_level.info': '信息日志',
- 'log_level.debug': '调试日志',
+ 'monitor.log_level.all_with_debug': '全部级别 (含DEBUG)',
+ 'monitor.log_level.error': '错误日志',
+ 'monitor.log_level.warning': '警告日志',
+ 'monitor.log_level.info': '信息日志',
+ 'monitor.log_level.debug': '调试日志',
// ============ DeadLetter ============
- 'dl.reprocess': '重新处理',
- 'dl.reprocess_success': 'DeadLetter重新处理成功',
- 'dl.reprocess_failed': 'DeadLetter重新处理失败',
+ 'monitor.dl.reprocess': '重新处理',
+ 'monitor.dl.reprocess_success': 'DeadLetter重新处理成功',
+ 'monitor.dl.reprocess_failed': 'DeadLetter重新处理失败',
// ============ 后端告警消息映射 ============
- 'alert.scheduler_not_running': '调度器未运行',
- 'alert.db_connection_failed': '数据库连接失败',
- 'alert.redis_connection_failed': 'Redis连接失败',
- 'alert.event_listener_stopped': '事件监听器已停止',
- 'alert.binlog_listener_stopped': 'Binlog监听器已停止',
- 'alert.high_error_rate': '错误率过高',
- 'alert.memory_warning': '内存使用警告',
- 'alert.cpu_warning': 'CPU使用警告',
+ 'monitor.alert.scheduler_not_running': '调度器未运行',
+ 'monitor.alert.db_connection_failed': '数据库连接失败',
+ 'monitor.alert.redis_connection_failed': 'Redis连接失败',
+ 'monitor.alert.event_listener_stopped': '事件监听器已停止',
+ 'monitor.alert.binlog_listener_stopped': 'Binlog监听器已停止',
+ 'monitor.alert.high_error_rate': '错误率过高',
+ 'monitor.alert.memory_warning': '内存使用警告',
+ 'monitor.alert.cpu_warning': 'CPU使用警告',
// ================================================================================
// MDS模块 (数据清洗管理系统)
@@ -659,5 +669,12 @@ window.__i18n_zh_CN__ = {
'mds.other.tip': '提示',
'mds.other.warning': '警告',
'mds.other.error': '错误',
- 'mds.other.success': '成功'
+ 'mds.other.success': '成功',
+
+ // ============ 补充翻译键 ============
+ 'monitor.other.yes': '是',
+ 'monitor.other.no': '否',
+ 'monitor.other.data_points': '个时间点',
+ 'monitor.col.is_slow': '是否慢请求',
+ 'monitor.col.is_error': '是否错误'
};
diff --git a/static/monitor/history-logs.html b/static/monitor/history-logs.html
index d1475a9..8151646 100644
--- a/static/monitor/history-logs.html
+++ b/static/monitor/history-logs.html
@@ -842,8 +842,8 @@
@@ -887,36 +887,36 @@
-
-
-
-
-
+
+
+
+
+
- 接收:
+ 接收:
0
- 发送:
+ 发送:
0
- 日志:
+ 日志:
0
|
- 级别:
- DEBUG:
+ 级别:
+ DEBUG:
0
- INFO:
+ INFO:
0
- WARNING:
+ WARNING:
0
- ERROR:
+ ERROR:
0
- CRITICAL:
+ CRITICAL:
0
@@ -966,11 +966,11 @@
@@ -1031,11 +1031,11 @@
-
+
-
+
-
+
-
@@ -1054,7 +1054,7 @@
-
+
@@ -1084,27 +1084,27 @@
- | 序号 |
- 时间 |
- 级别 |
- 模块 |
- 消息 |
- 操作 |
+ 序号 |
+ 时间 |
+ 级别 |
+ 模块 |
+ 消息 |
+ 操作 |
- | 暂无数据 |
+ | 暂无数据 |
@@ -1112,7 +1112,7 @@
-
暂无数据,请先执行查询
+
暂无数据,请先执行查询
@@ -1121,21 +1121,21 @@
-
请求量趋势
+ 📊 请求量趋势
-
日志级别分布
+ 📊 日志级别分布
-
状态码分布
+ 📈 状态码分布
-
慢请求TOP10
+ ⏱️ 慢请求TOP10
@@ -1181,7 +1181,7 @@
const endDate = new Date(endTime);
if (startDate >= endDate) {
- alert(i18n.t('error.time_range_invalid'));
+ alert(i18n.t('monitor.error.time_range_invalid'));
return;
}
}
@@ -1194,14 +1194,14 @@
try {
const badge = document.getElementById('history-query-badge');
if (badge) {
- badge.textContent = i18n.t('status.querying');
+ badge.textContent = i18n.t('monitor.status.querying');
badge.className = 'badge warning';
}
const queryBtn = document.getElementById('query-btn');
if (queryBtn) {
queryBtn.disabled = true;
- queryBtn.textContent = i18n.t('status.querying');
+ queryBtn.textContent = i18n.t('monitor.status.querying');
}
let url = `${API_BASE}/history/query?timezone_offset=${timezoneOffset}`;
@@ -1306,29 +1306,29 @@
switchResultTab(currentTab);
if (badge) {
- badge.textContent = i18n.t('success.query_complete');
+ badge.textContent = i18n.t('monitor.success.query_complete');
badge.className = 'badge healthy';
}
if (queryBtn) {
queryBtn.disabled = false;
- queryBtn.textContent = i18n.t('btn.query');
+ queryBtn.textContent = i18n.t('monitor.btn.query');
}
} catch (error) {
console.error('执行历史查询失败:', error);
- alert(i18n.t('error.query_failed'));
+ alert(i18n.t('monitor.error.query_failed'));
const badge = document.getElementById('history-query-badge');
if (badge) {
- badge.textContent = i18n.t('error.query_failed');
+ badge.textContent = i18n.t('monitor.error.query_failed');
badge.className = 'badge error';
}
const queryBtn = document.getElementById('query-btn');
if (queryBtn) {
queryBtn.disabled = false;
- queryBtn.textContent = i18n.t('btn.query');
+ queryBtn.textContent = i18n.t('monitor.btn.query');
}
}
}
@@ -1353,9 +1353,9 @@
document.getElementById('result-tabs').style.display = 'none';
- document.getElementById('http-results-body').innerHTML = '
| ' + i18n.t('status.no_data') + ' |
';
- document.getElementById('outbound-results-body').innerHTML = '
| ' + i18n.t('status.no_data') + ' |
';
- document.getElementById('logs-results-body').innerHTML = '
| ' + i18n.t('status.no_data') + ' |
';
+ document.getElementById('http-results-body').innerHTML = '
| ' + i18n.t('monitor.status.no_data') + ' |
';
+ document.getElementById('outbound-results-body').innerHTML = '
| ' + i18n.t('monitor.status.no_data') + ' |
';
+ document.getElementById('logs-results-body').innerHTML = '
| ' + i18n.t('monitor.status.no_data') + ' |
';
historyQueryData = { http: [], outbound: [], logs: [] };
// 清理缓存的排序数据
@@ -1365,7 +1365,7 @@
const badge = document.getElementById('history-query-badge');
if (badge) {
- badge.textContent = i18n.t('other.linked_query');
+ badge.textContent = i18n.t('monitor.other.linked_query');
badge.className = '';
}
}
@@ -1553,7 +1553,7 @@
const endTime = document.getElementById('history-end-time').value;
if (!startTime || !endTime) {
- alert(i18n.t('error.time_range_required'));
+ alert(i18n.t('monitor.error.time_range_required'));
return;
}
@@ -1630,25 +1630,93 @@
// 时间线渲染
let timelineData = [];
- async function loadTimeline() {
- const startTime = document.getElementById('history-start-time').value;
- const endTime = document.getElementById('history-end-time').value;
-
- if (!startTime || !endTime) return;
-
- let url = `${API_BASE}/history/timeline?start_time=${encodeURIComponent(startTime)}&end_time=${encodeURIComponent(endTime)}&limit=200`;
-
- const logLevel = document.getElementById('history-log-level').value;
- if (logLevel) url += `&level=${logLevel}`;
-
- try {
- const response = await fetch(url);
- const data = await response.json();
- timelineData = data.events || [];
+ function loadTimeline() {
+ // 复用已查询的数据,避免重复请求
+ if (!historyQueryData || (!historyQueryData.http?.length && !historyQueryData.outbound?.length && !historyQueryData.logs?.length)) {
+ timelineData = [];
renderTimeline();
- } catch (error) {
- console.error('加载时间线失败:', error);
+ return;
}
+
+ // 转换为统一事件格式(与后端逻辑一致)
+ const events = [];
+
+ // HTTP请求事件
+ for (const req of historyQueryData.http || []) {
+ events.push({
+ id: `http_${req.id}`,
+ timestamp: req.timestamp,
+ type: 'http',
+ icon: '📥',
+ level: req.status_code < 400 ? 'INFO' : 'ERROR',
+ summary: `${req.method} ${req.path}`,
+ detail: {
+ method: req.method,
+ path: req.path,
+ status_code: req.status_code,
+ duration: req.response_time,
+ client_ip: req.client_ip,
+ is_error: req.is_error,
+ is_slow: req.is_slow
+ }
+ });
+ }
+
+ // 发送请求事件
+ for (const req of historyQueryData.outbound || []) {
+ events.push({
+ id: `outbound_${req.id}`,
+ timestamp: req.timestamp,
+ type: 'outbound',
+ icon: '📤',
+ level: req.status_code < 400 ? 'INFO' : 'ERROR',
+ summary: `${req.method} ${req.url?.substring(0, 80) || ''}`,
+ detail: {
+ method: req.method,
+ url: req.url,
+ status_code: req.status_code,
+ duration: req.duration * 1000,
+ module: req.module,
+ is_error: req.is_error,
+ is_slow: req.is_slow
+ }
+ });
+ }
+
+ // 系统日志事件
+ const iconMap = {
+ 'DEBUG': '🔍',
+ 'INFO': '📝',
+ 'WARNING': '⚠️',
+ 'ERROR': '❌',
+ 'CRITICAL': '🔥'
+ };
+ for (const log of historyQueryData.logs || []) {
+ events.push({
+ id: `log_${log.id}`,
+ timestamp: log.timestamp,
+ type: 'log',
+ icon: iconMap[log.level] || '📝',
+ level: log.level,
+ summary: log.message?.substring(0, 100) || '',
+ detail: {
+ level: log.level,
+ module: log.module,
+ function: log.function,
+ line_number: log.line_number,
+ message: log.message,
+ stack_trace: log.stack_trace
+ }
+ });
+ }
+
+ // 按时间戳排序(降序)
+ events.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp));
+
+ // 应用限制(最多200条)
+ timelineData = events.slice(0, 200);
+
+ renderTimeline();
}
function renderTimeline() {
@@ -1656,25 +1724,63 @@
if (!container) return;
if (timelineData.length === 0) {
- container.innerHTML = '
' + i18n.t('status.no_data') + '
';
+ container.innerHTML = '
' + i18n.t('monitor.status.no_data') + '
';
return;
}
// 异常检测
const anomalies = detectAnomalies(timelineData);
- // 异常摘要
- let summaryHtml = '';
+ // 统计摘要(一次遍历完成所有统计)
+ const stats = { total: timelineData.length, http: 0, outbound: 0, log: 0, errors: 0, warnings: 0, slow: 0 };
+ for (const e of timelineData) {
+ if (e.type === 'http') stats.http++;
+ else if (e.type === 'outbound') stats.outbound++;
+ else if (e.type === 'log') stats.log++;
+
+ if (e.level === 'ERROR' || e.level === 'CRITICAL') stats.errors++;
+ else if (e.level === 'WARNING') stats.warnings++;
+
+ if (e.detail?.is_slow) stats.slow++;
+ }
+
+ // 统计摘要HTML
+ const summaryTitle = typeof i18n !== 'undefined' ? i18n.t('monitor.timeline.summary_title') : '📊 时间线统计';
+ const totalLabel = typeof i18n !== 'undefined' ? i18n.t('monitor.timeline.total_events') : '总事件';
+ const httpLabel = typeof i18n !== 'undefined' ? i18n.t('monitor.timeline.http_requests') : 'HTTP请求';
+ const outboundLabel = typeof i18n !== 'undefined' ? i18n.t('monitor.timeline.outbound_requests') : '发送请求';
+ const logLabel = typeof i18n !== 'undefined' ? i18n.t('monitor.timeline.system_logs') : '系统日志';
+ const errorLabel = typeof i18n !== 'undefined' ? i18n.t('monitor.timeline.errors') : '错误';
+ const warningLabel = typeof i18n !== 'undefined' ? i18n.t('monitor.timeline.warnings') : '警告';
+ const slowLabel = typeof i18n !== 'undefined' ? i18n.t('monitor.timeline.slow_requests') : '慢请求';
+
+ let summaryHtml = `
+
${summaryTitle}
+
+ ${stats.total} ${totalLabel}
+ 📥 ${stats.http} ${httpLabel}
+ 📤 ${stats.outbound} ${outboundLabel}
+ 📝 ${stats.log} ${logLabel}
+ ${stats.errors > 0 ? `❌ ${stats.errors} ${errorLabel}` : ''}
+ ${stats.warnings > 0 ? `⚠️ ${stats.warnings} ${warningLabel}` : ''}
+ ${stats.slow > 0 ? `⏱️ ${stats.slow} ${slowLabel}` : ''}
+
+
`;
+
+ // 异常告警HTML(如果有异常)
if (anomalies.length > 0) {
- summaryHtml = `
-
⚠️ 发现 ${anomalies.length} 处异常
+ const anomalyText = typeof i18n !== 'undefined' ?
+ i18n.t('monitor.timeline.anomaly_detected', {count: anomalies.length}) :
+ `发现 ${anomalies.length} 处异常`;
+ summaryHtml += `
+
⚠️ ${anomalyText}
${anomalies.map(a => a.description).join('、')}
`;
}
- container.innerHTML = summaryHtml + timelineData.map(event => {
+ const renderedHtml = timelineData.map(event => {
const isError = event.level === 'ERROR' || event.level === 'CRITICAL';
const isWarning = event.level === 'WARNING';
const isAnomaly = anomalies.some(a => a.eventId === event.id);
@@ -1692,6 +1798,8 @@
`;
}).join('');
+
+ container.innerHTML = summaryHtml + renderedHtml;
}
// 异常检测算法
@@ -1726,10 +1834,13 @@
const threshold = avg * 3;
events.forEach(e => {
if (e.type === 'http' && e.detail?.duration > threshold) {
+ const slowReqText = typeof i18n !== 'undefined' ?
+ i18n.t('monitor.timeline.slow_anomaly', {duration: e.detail.duration.toFixed(0), avg: avg.toFixed(0)}) :
+ `异常慢请求(${e.detail.duration.toFixed(0)}ms > 平均${avg.toFixed(0)}ms×3)`;
anomalies.push({
eventId: e.id,
type: 'slow_anomaly',
- description: `异常慢请求(${e.detail.duration.toFixed(0)}ms > 平均${avg.toFixed(0)}ms×3)`
+ description: slowReqText
});
}
});
@@ -1746,10 +1857,13 @@
});
Object.entries(errorMessages).forEach(([msg, ids]) => {
if (ids.length >= 3) {
+ const dupErrorText = typeof i18n !== 'undefined' ?
+ i18n.t('monitor.timeline.duplicate_error', {msg: msg.substring(0, 20), count: ids.length}) :
+ `重复错误"${msg.substring(0, 20)}..."出现${ids.length}次`;
anomalies.push({
eventId: ids[ids.length - 1],
type: 'duplicate_error',
- description: `重复错误"${msg.substring(0, 20)}..."出现${ids.length}次`
+ description: dupErrorText
});
}
});
@@ -1769,7 +1883,7 @@
const endTime = document.getElementById('history-end-time').value;
if (!startTime || !endTime) {
- document.getElementById('tab-chart').innerHTML = '
' + i18n.t('chart.query_required') + '
';
+ document.getElementById('tab-chart').innerHTML = '
' + i18n.t('monitor.chart.query_required') + '
';
return;
}
@@ -1797,8 +1911,9 @@
`;
- }).join('') || '
无数据
';
- levelCanvas.parentElement.innerHTML = '
📊 日志级别分布
' + levelHtml + '
';
+ }).join('') || `
${typeof i18n !== 'undefined' ? i18n.t('monitor.chart.no_data') : '无数据'}
`;
+ const levelTitle = typeof i18n !== 'undefined' ? i18n.t('monitor.chart.level_distribution') : '📊 日志级别分布';
+ levelCanvas.parentElement.innerHTML = `
${levelTitle}
` + levelHtml + '
';
}
// 渲染状态码分布(饼图样式)
@@ -1815,8 +1930,9 @@
${pct}%
${k} (${v})
`;
- }).join('') || '
无数据
';
- statusCanvas.parentElement.innerHTML = '
📈 状态码分布
' + statusHtml + '
';
+ }).join('') || `
${typeof i18n !== 'undefined' ? i18n.t('monitor.chart.no_data') : '无数据'}
`;
+ const statusTitle = typeof i18n !== 'undefined' ? i18n.t('monitor.chart.status_distribution') : '📈 状态码分布';
+ statusCanvas.parentElement.innerHTML = `
${statusTitle}
` + statusHtml + '
';
}
// 渲染慢请求TOP10(表格)
@@ -1838,8 +1954,9 @@
`).join('')}
`
- : '
✅ 无慢请求
';
- slowCanvas.parentElement.innerHTML = '
⏱️ 慢请求TOP10
' + slowHtml;
+ : `
✅ ${typeof i18n !== 'undefined' ? i18n.t('monitor.chart.no_slow') : '无慢请求'}
`;
+ const slowTitle = typeof i18n !== 'undefined' ? i18n.t('monitor.chart.slow_requests') : '⏱️ 慢请求TOP10';
+ slowCanvas.parentElement.innerHTML = `
${slowTitle}
` + slowHtml;
}
// 渲染趋势摘要
@@ -1850,23 +1967,24 @@
const trendHtml = trend.length > 0
? `
-
${summary.total_requests || 0}
总请求
-
${summary.error_count || 0}
错误
-
${summary.slow_count || 0}
慢请求
+
${summary.total_requests || 0}
${typeof i18n !== 'undefined' ? i18n.t('monitor.chart.total_requests') : '总请求'}
+
${summary.error_count || 0}
${typeof i18n !== 'undefined' ? i18n.t('monitor.chart.error_count') : '错误'}
+
${summary.slow_count || 0}
${typeof i18n !== 'undefined' ? i18n.t('monitor.chart.slow_count') : '慢请求'}
- 时间范围: ${trend.length}个时间点 | 日志总数: ${summary.total_logs || 0}条
+ ${typeof i18n !== 'undefined' ? i18n.t('monitor.stats.time_range') : '时间范围'}: ${trend.length}${typeof i18n !== 'undefined' ? i18n.t('monitor.other.data_points') : '个时间点'} | ${typeof i18n !== 'undefined' ? i18n.t('monitor.chart.log_count') : '日志总数'}: ${summary.total_logs || 0}${typeof i18n !== 'undefined' ? i18n.t('monitor.pagination.items') : '条'}
`
- : '
无趋势数据
';
- trendCanvas.parentElement.innerHTML = '
📊 请求量趋势
' + trendHtml;
+ : `
${typeof i18n !== 'undefined' ? i18n.t('monitor.chart.no_trend') : '无趋势数据'}
`;
+ const trendTitle = typeof i18n !== 'undefined' ? i18n.t('monitor.chart.request_trend') : '📊 请求量趋势';
+ trendCanvas.parentElement.innerHTML = `
${trendTitle}
` + trendHtml;
}
} catch (error) {
console.error('渲染图表失败:', error);
const chartContainer = document.querySelector('.chart-container');
if (chartContainer) {
- chartContainer.innerHTML = '
' + i18n.t('chart.load_failed') + '
';
+ chartContainer.innerHTML = '
' + i18n.t('monitor.chart.load_failed') + '
';
}
}
}
@@ -1882,11 +2000,11 @@
const btn = document.getElementById('realtime-btn');
if (realtimeTracking) {
- btn.textContent = i18n.t('btn.realtime_on');
+ btn.textContent = i18n.t('monitor.btn.realtime_on');
btn.className = 'btn btn-primary';
startRealtimeTracking();
} else {
- btn.textContent = i18n.t('btn.realtime_off');
+ btn.textContent = i18n.t('monitor.btn.realtime_off');
btn.className = 'btn btn-secondary';
stopRealtimeTracking();
}
@@ -1902,7 +2020,7 @@
// 10分钟自动暂停
if (trackingDuration >= 600) {
stopRealtimeTracking();
- alert(i18n.t('error.auto_pause'));
+ alert(i18n.t('monitor.error.auto_pause'));
return;
}
@@ -1938,7 +2056,7 @@
}
realtimeTracking = false;
const btn = document.getElementById('realtime-btn');
- btn.textContent = i18n.t('btn.realtime_off');
+ btn.textContent = i18n.t('monitor.btn.realtime_off');
btn.className = 'btn btn-secondary';
}
@@ -1964,14 +2082,14 @@
if (!select) return;
const templates = getTemplates();
- select.innerHTML = '
' +
+ select.innerHTML = '
' +
templates.map((t, i) => `
`).join('');
}
function saveQueryTemplate() {
const templates = getTemplates();
if (templates.length >= 10) {
- alert(i18n.t('error.max_templates'));
+ alert(i18n.t('monitor.error.max_templates'));
return;
}
@@ -1997,7 +2115,7 @@
templates.push(template);
saveTemplates(templates);
- alert(i18n.t('success.template_saved', {name: name}));
+ alert(i18n.t('monitor.success.template_saved', {name: name}));
}
function loadQueryTemplate() {
@@ -2133,7 +2251,7 @@
if (!tbody) return;
if (historyQueryData.http.length === 0) {
- tbody.innerHTML = '
| ' + i18n.t('status.no_data') + ' |
';
+ tbody.innerHTML = '
| ' + i18n.t('monitor.status.no_data') + ' |
';
sortedHttpData = [];
return;
}
@@ -2170,7 +2288,7 @@
if (!tbody) return;
if (historyQueryData.outbound.length === 0) {
- tbody.innerHTML = '
| ' + i18n.t('status.no_data') + ' |
';
+ tbody.innerHTML = '
| ' + i18n.t('monitor.status.no_data') + ' |
';
sortedOutboundData = [];
return;
}
@@ -2207,7 +2325,7 @@
if (!tbody) return;
if (historyQueryData.logs.length === 0) {
- tbody.innerHTML = '
| ' + i18n.t('status.no_data') + ' |
';
+ tbody.innerHTML = '
| ' + i18n.t('monitor.status.no_data') + ' |
';
sortedLogsData = [];
return;
}
@@ -2255,26 +2373,40 @@
return pairs.length > 0 ? '?' + pairs.join('&') : '-';
}
- function showHistoryHttpDetail(index) {
+ async function showHistoryHttpDetail(index) {
const req = sortedHttpData[index];
if (!req) return;
+ // 懒加载:如果数据不包含request_body,则从API加载完整数据
+ let detailData = req;
+ if (!req.hasOwnProperty('request_body')) {
+ try {
+ const response = await fetch(`${API_BASE}/history/http/${req.id}`);
+ detailData = await response.json();
+ } catch (error) {
+ console.error('加载详情失败:', error);
+ alert(i18n.t('monitor.error.detail_load_failed') || '加载详情失败');
+ return;
+ }
+ }
+
const detail = `
- | 请求ID | ${req.id || '-'} |
- | 时间戳 | ${req.timestamp || '-'} |
- | 请求方法 | ${req.method || '-'} |
- | 请求路径 | ${req.path || '-'} |
- | 查询参数 | ${formatQueryParams(req.query_params)} |
- | 状态码 | ${req.status_code || '-'} |
- | 响应时间 | ${req.duration ? req.duration.toFixed(2) + 'ms' : '-'} |
- | 客户端IP | ${req.client_ip || '-'} |
- | User Agent | ${req.user_agent || '-'} |
- | 是否慢请求 | ${req.is_slow ? '是' : '否'} |
- | 是否错误 | ${req.is_error ? '是' : '否'} |
- | 错误信息 | ${req.error_message || '-'} |
- | 请求体 | ${req.request_body || '-'} |
+ | 请求ID | ${detailData.id || '-'} |
+ | 时间戳 | ${detailData.timestamp || '-'} |
+ | 请求方法 | ${detailData.method || '-'} |
+ | 请求路径 | ${detailData.path || '-'} |
+ | 查询参数 | ${formatQueryParams(detailData.query_params)} |
+ | 状态码 | ${detailData.status_code || '-'} |
+ | 响应时间 | ${detailData.duration ? detailData.duration.toFixed(2) + 'ms' : '-'} |
+ | 客户端IP | ${detailData.client_ip || '-'} |
+ | User Agent | ${detailData.user_agent || '-'} |
+ | ${typeof i18n !== 'undefined' ? i18n.t('monitor.col.is_slow') : '是否慢请求'} | ${detailData.is_slow ? (typeof i18n !== 'undefined' ? i18n.t('monitor.other.yes') : '是') : (typeof i18n !== 'undefined' ? i18n.t('monitor.other.no') : '否')} |
+ | ${typeof i18n !== 'undefined' ? i18n.t('monitor.col.is_error') : '是否错误'} | ${detailData.is_error ? (typeof i18n !== 'undefined' ? i18n.t('monitor.other.yes') : '是') : (typeof i18n !== 'undefined' ? i18n.t('monitor.other.no') : '否')} |
+ | ${typeof i18n !== 'undefined' ? i18n.t('monitor.col.error_message') : '错误信息'} | ${detailData.error_message || '-'} |
+ | 请求体 | ${detailData.request_body || '-'} |
+ | 响应体 | ${detailData.response_body || '-'} |
`;
@@ -2282,25 +2414,38 @@
showDetailModal('接收请求详情', detail);
}
- function showHistoryOutboundDetail(index) {
+ async function showHistoryOutboundDetail(index) {
const req = sortedOutboundData[index];
if (!req) return;
+ // 懒加载:如果数据不包含request_body,则从API加载完整数据
+ let detailData = req;
+ if (!req.hasOwnProperty('request_body')) {
+ try {
+ const response = await fetch(`${API_BASE}/history/outbound/${req.id}`);
+ detailData = await response.json();
+ } catch (error) {
+ console.error('加载详情失败:', error);
+ alert(i18n.t('monitor.error.detail_load_failed') || '加载详情失败');
+ return;
+ }
+ }
+
const detail = `
- | 请求ID | ${req.id || '-'} |
- | 时间戳 | ${req.timestamp || '-'} |
- | 请求方法 | ${req.method || '-'} |
- | 请求URL | ${req.url || '-'} |
- | 状态码 | ${req.status_code || '-'} |
- | 响应时间 | ${req.duration ? req.duration.toFixed(2) + 'ms' : '-'} |
- | 模块 | ${req.module || '-'} |
- | 是否慢请求 | ${req.is_slow ? '是' : '否'} |
- | 是否错误 | ${req.is_error ? '是' : '否'} |
- | 错误信息 | ${req.error_message || '-'} |
- | 请求体 | ${req.request_body || '-'} |
- | 响应体 | ${req.response_body || '-'} |
+ | 请求ID | ${detailData.id || '-'} |
+ | 时间戳 | ${detailData.timestamp || '-'} |
+ | 请求方法 | ${detailData.method || '-'} |
+ | 请求URL | ${detailData.url || '-'} |
+ | 状态码 | ${detailData.status_code || '-'} |
+ | 响应时间 | ${detailData.duration ? detailData.duration.toFixed(2) + 'ms' : '-'} |
+ | 模块 | ${detailData.module || '-'} |
+ | ${typeof i18n !== 'undefined' ? i18n.t('monitor.col.is_slow') : '是否慢请求'} | ${detailData.is_slow ? (typeof i18n !== 'undefined' ? i18n.t('monitor.other.yes') : '是') : (typeof i18n !== 'undefined' ? i18n.t('monitor.other.no') : '否')} |
+ | ${typeof i18n !== 'undefined' ? i18n.t('monitor.col.is_error') : '是否错误'} | ${detailData.is_error ? (typeof i18n !== 'undefined' ? i18n.t('monitor.other.yes') : '是') : (typeof i18n !== 'undefined' ? i18n.t('monitor.other.no') : '否')} |
+ | ${typeof i18n !== 'undefined' ? i18n.t('monitor.col.error_message') : '错误信息'} | ${detailData.error_message || '-'} |
+ | 请求体 | ${detailData.request_body || '-'} |
+ | 响应体 | ${detailData.response_body || '-'} |
`;
@@ -2308,20 +2453,34 @@
showDetailModal('发送请求详情', detail);
}
- function showHistoryLogDetail(index) {
+ async function showHistoryLogDetail(index) {
const log = sortedLogsData[index];
if (!log) return;
+ // 懒加载:如果数据不包含stack_trace,则从API加载完整数据
+ let detailData = log;
+ if (!log.hasOwnProperty('stack_trace')) {
+ try {
+ const response = await fetch(`${API_BASE}/history/log/${log.id}`);
+ detailData = await response.json();
+ } catch (error) {
+ console.error('加载详情失败:', error);
+ alert(i18n.t('monitor.error.detail_load_failed') || '加载详情失败');
+ return;
+ }
+ }
+
const detail = `
- | 日志ID | ${log.id || '-'} |
- | 时间戳 | ${log.timestamp || '-'} |
- | 日志级别 | ${log.level || '-'} |
- | 模块 | ${log.module || '-'} |
- | 函数 | ${log.function || '-'} |
- | 行号 | ${log.line_number || '-'} |
- | 消息 | ${log.message || '-'} |
+ | 日志ID | ${detailData.id || '-'} |
+ | 时间戳 | ${detailData.timestamp || '-'} |
+ | 日志级别 | ${detailData.level || '-'} |
+ | 模块 | ${detailData.module || '-'} |
+ | 函数 | ${detailData.function || '-'} |
+ | 行号 | ${detailData.line_number || '-'} |
+ | 消息 | ${detailData.message || '-'} |
+ ${detailData.stack_trace ? `| 堆栈 | ${detailData.stack_trace} |
` : ''}
`;
diff --git a/static/monitor/index.html b/static/monitor/index.html
index 0f3e067..6ef2fa5 100644
--- a/static/monitor/index.html
+++ b/static/monitor/index.html
@@ -16,13 +16,13 @@
@@ -41,20 +41,20 @@
-
内存使用
+
内存使用
-- MB
--%
@@ -64,11 +64,11 @@
@@ -81,21 +81,21 @@
- 总连接数
+ 总连接数
--
- 健康
+ 健康
--
- 异常
+ 异常
--
@@ -108,8 +108,8 @@
@@ -121,21 +121,21 @@
- 总请求数
+ 总请求数
--
- 错误率
+ 错误率
--%
- 平均响应
+ 平均响应
--ms
@@ -149,21 +149,21 @@
- 总请求数
+ 总请求数
--
- 错误率
+ 错误率
--%
- 平均响应
+ 平均响应
--ms
@@ -173,17 +173,17 @@
- 调度器状态
+ 调度器状态
--
- 任务数量
+ 任务数量
--
@@ -196,8 +196,8 @@
@@ -205,17 +205,17 @@
- | 账套名称 |
- 连接状态 |
- 最后检查 |
- 当前连接 |
- 最大连接 |
- 最小连接 |
- 空闲连接 |
- 使用中连接 |
- 使用率 |
- 处理记录 |
- 次数 |
+ 账套名称 |
+ 连接状态 |
+ 最后检查 |
+ 当前连接 |
+ 最大连接 |
+ 最小连接 |
+ 空闲连接 |
+ 使用中连接 |
+ 使用率 |
+ 处理记录 |
+ 次数 |
@@ -229,62 +229,62 @@
-
Redis 连接池使用情况
+ Redis 连接池使用情况
-
Redis 缓冲大小变化
+ Redis 缓冲大小变化
@@ -295,7 +295,7 @@
@@ -318,15 +318,15 @@
- | 序号 |
- 时间戳 |
- 方法 |
- 端点 |
- 查询参数 |
- 状态码 |
- 响应时间 |
- 客户端IP |
- 错误信息 |
+ 序号 |
+ 时间戳 |
+ 方法 |
+ 端点 |
+ 查询参数 |
+ 状态码 |
+ 响应时间 |
+ 客户端IP |
+ 错误信息 |
@@ -381,9 +381,9 @@
@@ -397,7 +397,7 @@
@@ -421,14 +421,14 @@
- | 序号 |
- 时间戳 |
- 方法 |
- URL |
- 状态码 |
- 响应时间 |
- 模块 |
- 错误信息 |
+ 序号 |
+ 时间戳 |
+ 方法 |
+ URL |
+ 状态码 |
+ 响应时间 |
+ 模块 |
+ 错误信息 |
@@ -447,23 +447,23 @@
@@ -471,12 +471,12 @@
- | 序号 |
- 时间 |
- 级别 |
- 模块 |
- 消息 |
- 已读 |
+ 序号 |
+ 时间 |
+ 级别 |
+ 模块 |
+ 消息 |
+ 已读 |
@@ -493,55 +493,55 @@
- 总接收事件
+ 总接收事件
--
- 已处理事件
+ 已处理事件
--
- 中断处理
+ 中断处理
--
- 待处理
+ 待处理
--
- 整体成功率
+ 整体成功率
--%
- 活跃事件类型
+ 活跃事件类型
--
- 背压状态
+ 背压状态
正常
- 待处理事件
+ 待处理事件
--
- 背压使用率
+ 背压使用率
--%
- 事件循环状态
+ 事件循环状态
正常
@@ -549,17 +549,17 @@
- | 描述 |
- 总接收 |
- 待处理 |
- 已处理 |
- 中断 |
- 完成率 |
- 平均延迟 |
- 操作 |
- 最后动作 |
- 状态 |
- 操作 |
+ 描述 |
+ 总接收 |
+ 待处理 |
+ 已处理 |
+ 中断 |
+ 完成率 |
+ 平均延迟 |
+ 操作 |
+ 最后动作 |
+ 状态 |
+ 操作 |
@@ -577,21 +577,21 @@
@@ -601,25 +601,25 @@
@@ -633,40 +633,40 @@
- DL总数
+ DL总数
--
- 最近DL
+ 最近DL
--
- 处理成功率
+ 处理成功率
--%
@@ -674,13 +674,13 @@
- | ID |
- 事件类型 |
- 时间 |
- 数据库 |
- 表 |
- 错误信息 |
- 操作 |
+ ID |
+ 事件类型 |
+ 时间 |
+ 数据库 |
+ 表 |
+ 错误信息 |
+ 操作 |
diff --git a/static/monitor/js/monitor.js b/static/monitor/js/monitor.js
index 8c9c28b..02a3fab 100644
--- a/static/monitor/js/monitor.js
+++ b/static/monitor/js/monitor.js
@@ -330,7 +330,7 @@ function initWebSocket() {
// 关闭现有连接
if (ws) {
try {
- ws.close(1000, typeof i18n !== 'undefined' ? i18n.t('connection.reconnecting') : '重新连接');
+ ws.close(1000, typeof i18n !== 'undefined' ? i18n.t('monitor.connection.reconnecting') : '重新连接');
} catch (error) {
console.error('关闭 WebSocket 连接失败:', error);
}
@@ -660,7 +660,7 @@ async function refreshAll() {
// 检查系统状态是否为错误(只有连接失败才算是系统错误)
const statusIndicator = getElement('status-indicator');
const statusText = statusIndicator ? statusIndicator.textContent : '';
- const failedText = typeof i18n !== 'undefined' ? i18n.t('connection.failed') : '连接失败';
+ const failedText = typeof i18n !== 'undefined' ? i18n.t('monitor.connection.failed') : '连接失败';
const isSystemError = statusIndicator && statusIndicator.classList.contains('error') && statusText.includes(failedText);
// 如果系统状态不是连接失败,继续获取其他数据
@@ -844,7 +844,7 @@ async function fetchHealth() {
updateHealthStatus(data);
} catch (error) {
console.error('获取健康状态失败:', error);
- setSystemStatus('error', typeof i18n !== 'undefined' ? i18n.t('connection.failed') : '连接失败');
+ setSystemStatus('error', typeof i18n !== 'undefined' ? i18n.t('monitor.connection.failed') : '连接失败');
}
}
@@ -852,9 +852,9 @@ async function fetchHealth() {
function updateHealthStatus(data) {
const indicator = getElement('status-indicator');
const statusMap = {
- 'healthy': { text: typeof i18n !== 'undefined' ? i18n.t('status.healthy') : '● 系统正常', class: 'healthy' },
- 'degraded': { text: typeof i18n !== 'undefined' ? i18n.t('status.partial_warnings') : '● 部分警告', class: 'warning' },
- 'unhealthy': { text: typeof i18n !== 'undefined' ? i18n.t('status.unhealthy') : '● 系统异常', class: 'error' }
+ 'healthy': { text: typeof i18n !== 'undefined' ? i18n.t('monitor.status.healthy') : '● 系统正常', class: 'healthy' },
+ 'degraded': { text: typeof i18n !== 'undefined' ? i18n.t('monitor.status.partial_warnings') : '● 部分警告', class: 'warning' },
+ 'unhealthy': { text: typeof i18n !== 'undefined' ? i18n.t('monitor.status.unhealthy') : '● 系统异常', class: 'error' }
};
const status = statusMap[data.status] || statusMap['unhealthy'];
@@ -897,7 +897,7 @@ async function fetchResource() {
function updateResourceDisplay(data, networkUpload = 0, networkDownload = 0) {
if (data.error) {
const resourceBadge = getElement('resource-badge');
- resourceBadge.textContent = typeof i18n !== 'undefined' ? i18n.t('metric.failed') : '错误';
+ resourceBadge.textContent = typeof i18n !== 'undefined' ? i18n.t('monitor.metric.failed') : '错误';
resourceBadge.className = 'badge error';
return;
}
@@ -929,7 +929,7 @@ function updateResourceDisplay(data, networkUpload = 0, networkDownload = 0) {
// 更新徽章
const badge = getElement('resource-badge');
- badge.textContent = typeof i18n !== 'undefined' ? i18n.t('status.running') : '运行中';
+ badge.textContent = typeof i18n !== 'undefined' ? i18n.t('monitor.status.running') : '运行中';
badge.className = 'badge healthy';
}
@@ -982,7 +982,7 @@ function initResourceChart() {
labels: [],
datasets: [
{
- label: typeof i18n !== 'undefined' ? i18n.t('chart.cpu') : 'CPU',
+ label: typeof i18n !== 'undefined' ? i18n.t('monitor.chart.cpu') : 'CPU',
data: [],
borderColor: '#d39102ff',
backgroundColor: 'rgba(184, 134, 11, 0.1)',
@@ -991,7 +991,7 @@ function initResourceChart() {
yAxisID: 'y'
},
{
- label: typeof i18n !== 'undefined' ? i18n.t('chart.memory') : '内存',
+ label: typeof i18n !== 'undefined' ? i18n.t('monitor.chart.memory') : '内存',
data: [],
borderColor: '#9932cc',
backgroundColor: 'rgba(153, 50, 204, 0.1)',
@@ -1000,7 +1000,7 @@ function initResourceChart() {
yAxisID: 'y'
},
{
- label: typeof i18n !== 'undefined' ? i18n.t('chart.upload') : '上传',
+ label: typeof i18n !== 'undefined' ? i18n.t('monitor.chart.upload') : '上传',
data: [],
borderColor: '#87ceeb',
backgroundColor: 'rgba(135, 206, 235, 0.1)',
@@ -1009,7 +1009,7 @@ function initResourceChart() {
yAxisID: 'y1'
},
{
- label: typeof i18n !== 'undefined' ? i18n.t('chart.download') : '下载',
+ label: typeof i18n !== 'undefined' ? i18n.t('monitor.chart.download') : '下载',
data: [],
borderColor: '#98fb98',
backgroundColor: 'rgba(152, 251, 152, 0.1)',
@@ -1031,7 +1031,7 @@ function initResourceChart() {
max: 100,
title: {
display: true,
- text: typeof i18n !== 'undefined' ? i18n.t('chart.cpu_memory_axis') : 'CPU / 内存 (%)'
+ text: typeof i18n !== 'undefined' ? i18n.t('monitor.chart.cpu_memory_axis') : 'CPU / 内存 (%)'
}
},
y1: {
@@ -1041,7 +1041,7 @@ function initResourceChart() {
beginAtZero: true,
title: {
display: true,
- text: typeof i18n !== 'undefined' ? i18n.t('chart.network_axis') : '网络上传 / 下载 (KB/s)'
+ text: typeof i18n !== 'undefined' ? i18n.t('monitor.chart.network_axis') : '网络上传 / 下载 (KB/s)'
},
grid: {
drawOnChartArea: false
@@ -1108,10 +1108,10 @@ function updateDatabaseDisplay(data) {
// 更新徽章
const badge = getElement('db-badge');
if (summary.unhealthy === 0) {
- badge.textContent = typeof i18n !== 'undefined' ? i18n.t('metric.healthy') : '正常';
+ badge.textContent = typeof i18n !== 'undefined' ? i18n.t('monitor.metric.healthy') : '正常';
badge.className = 'badge healthy';
} else {
- badge.textContent = typeof i18n !== 'undefined' ? i18n.t('metric.unhealthy') : '异常';
+ badge.textContent = typeof i18n !== 'undefined' ? i18n.t('monitor.metric.unhealthy') : '异常';
badge.className = 'badge warning';
}
@@ -1126,7 +1126,7 @@ function updateDatabaseDisplay(data) {
const dbConnections = connections.connections || {};
if (Object.keys(dbConnections).length === 0) {
- listEl.innerHTML = '' + (typeof i18n !== 'undefined' ? i18n.t('status.no_db_connections') : '暂无数据库连接') + '
';
+ listEl.innerHTML = '' + (typeof i18n !== 'undefined' ? i18n.t('monitor.status.no_db_connections') : '暂无数据库连接') + '
';
checkAlertConditions();
return;
}
@@ -1140,7 +1140,7 @@ function updateDatabaseDisplay(data) {
${name === mainDb ? 'Ⓜ️ ' + name : name}
- ${status.healthy ? (typeof i18n !== 'undefined' ? i18n.t('metric.healthy') : '正常') : (status.error || (typeof i18n !== 'undefined' ? i18n.t('metric.unhealthy') : '异常'))}
+ ${status.healthy ? (typeof i18n !== 'undefined' ? i18n.t('monitor.metric.healthy') : '正常') : (status.error || (typeof i18n !== 'undefined' ? i18n.t('monitor.metric.unhealthy') : '异常'))}
`;
fragment.appendChild(dbItem);
@@ -1174,14 +1174,14 @@ function updateSchedulerDisplay(data) {
const badge = getElement('scheduler-badge');
if (scheduler.running) {
- statusEl.textContent = typeof i18n !== 'undefined' ? i18n.t('status.running') : '运行中';
+ statusEl.textContent = typeof i18n !== 'undefined' ? i18n.t('monitor.status.running') : '运行中';
statusEl.style.color = '#52c41a';
- badge.textContent = typeof i18n !== 'undefined' ? i18n.t('status.running') : '运行中';
+ badge.textContent = typeof i18n !== 'undefined' ? i18n.t('monitor.status.running') : '运行中';
badge.className = 'badge healthy';
} else {
- statusEl.textContent = typeof i18n !== 'undefined' ? i18n.t('status.stopped') : '已停止';
+ statusEl.textContent = typeof i18n !== 'undefined' ? i18n.t('monitor.status.stopped') : '已停止';
statusEl.style.color = '#faad14';
- badge.textContent = typeof i18n !== 'undefined' ? i18n.t('status.stopped') : '已停止';
+ badge.textContent = typeof i18n !== 'undefined' ? i18n.t('monitor.status.stopped') : '已停止';
badge.className = 'badge warning';
}
@@ -1192,7 +1192,7 @@ function updateSchedulerDisplay(data) {
const listEl = getElement('job-list');
if (listEl) {
if (jobs.length === 0) {
- listEl.innerHTML = '' + (typeof i18n !== 'undefined' ? i18n.t('status.no_scheduler') : '暂无定时任务') + '
';
+ listEl.innerHTML = '' + (typeof i18n !== 'undefined' ? i18n.t('monitor.status.no_scheduler') : '暂无定时任务') + '
';
return;
}
@@ -1206,7 +1206,7 @@ function updateSchedulerDisplay(data) {
${job.name || job.id}
${job.trigger}
- ${job.next_run_time ? formatDateTime(job.next_run_time) : (typeof i18n !== 'undefined' ? i18n.t('scheduler.not_scheduled') : '未计划')}
+ ${job.next_run_time ? formatDateTime(job.next_run_time) : (typeof i18n !== 'undefined' ? i18n.t('monitor.scheduler.not_scheduled') : '未计划')}
`;
fragment.appendChild(jobItem);
});
@@ -1219,7 +1219,7 @@ function updateSchedulerDisplay(data) {
// 格式化日期时间
function formatDateTime(dateValue) {
- if (!dateValue) return typeof i18n !== 'undefined' ? i18n.t('scheduler.never_run') : '从未执行';
+ if (!dateValue) return typeof i18n !== 'undefined' ? i18n.t('monitor.scheduler.never_run') : '从未执行';
// 检查缓存
const cacheKey = `datetime_${dateValue}`;
@@ -1240,7 +1240,7 @@ function formatDateTime(dateValue) {
}
if (isNaN(date.getTime())) {
- return typeof i18n !== 'undefined' ? i18n.t('scheduler.never_run') : '从未执行';
+ return typeof i18n !== 'undefined' ? i18n.t('monitor.scheduler.never_run') : '从未执行';
}
const result = `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')} ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`;
@@ -1310,13 +1310,13 @@ function updateHTTPDisplay(data) {
const badge = getElement('http-badge');
const errorRateValue = summary.error_rate || 0;
if (errorRateValue < 1) {
- badge.textContent = typeof i18n !== 'undefined' ? i18n.t('metric.healthy') : '正常';
+ badge.textContent = typeof i18n !== 'undefined' ? i18n.t('monitor.metric.healthy') : '正常';
badge.className = 'badge healthy';
} else if (errorRateValue < 5) {
- badge.textContent = typeof i18n !== 'undefined' ? i18n.t('alert.warning') : '警告';
+ badge.textContent = typeof i18n !== 'undefined' ? i18n.t('monitor.alert.warning') : '警告';
badge.className = 'badge warning';
} else {
- badge.textContent = typeof i18n !== 'undefined' ? i18n.t('metric.unhealthy') : '异常';
+ badge.textContent = typeof i18n !== 'undefined' ? i18n.t('monitor.metric.unhealthy') : '异常';
badge.className = 'badge error';
}
@@ -1340,7 +1340,7 @@ function updateHTTPDisplay(data) {
const statusCodes = data.status_codes || {};
if (Object.keys(statusCodes).length === 0) {
- statusCodesEl.innerHTML = '' + (typeof i18n !== 'undefined' ? i18n.t('status.no_http_requests') : '暂无接收请求') + '
';
+ statusCodesEl.innerHTML = '' + (typeof i18n !== 'undefined' ? i18n.t('monitor.status.no_http_requests') : '暂无接收请求') + '
';
} else {
statusCodesEl.innerHTML = Object.entries(statusCodes).map(([code, count]) => {
const codeClass = code.startsWith('2') ? 'success' : (code.startsWith('3') ? 'redirect' : 'error');
@@ -1371,9 +1371,9 @@ function updateHTTPDisplay(data) {
${path}
- ${typeof i18n !== 'undefined' ? i18n.t('metric.avg_time') : '平均'}: ${(stats.avg_time * 1000).toFixed(0)}ms
- ${stats.errors > 0 ? `${typeof i18n !== 'undefined' ? i18n.t('metric.failed') : '错误'}: ${stats.errors}` : ''}
- ${stats.slow_requests > 0 ? `${typeof i18n !== 'undefined' ? i18n.t('metric.requests_slow') : '慢请求'}: ${stats.slow_requests}` : ''}
+ ${typeof i18n !== 'undefined' ? i18n.t('monitor.metric.avg_time') : '平均'}: ${(stats.avg_time * 1000).toFixed(0)}ms
+ ${stats.errors > 0 ? `${typeof i18n !== 'undefined' ? i18n.t('monitor.metric.failed') : '错误'}: ${stats.errors}` : ''}
+ ${stats.slow_requests > 0 ? `${typeof i18n !== 'undefined' ? i18n.t('monitor.metric.requests_slow') : '慢请求'}: ${stats.slow_requests}` : ''}
${stats.count}
@@ -1388,7 +1388,7 @@ function updateAlertsDisplay(alerts) {
const listEl = getElement('alert-list');
if (alerts.length === 0) {
- listEl.innerHTML = '' + (typeof i18n !== 'undefined' ? i18n.t('status.no_alerts') : '暂无告警') + '
';
+ listEl.innerHTML = '' + (typeof i18n !== 'undefined' ? i18n.t('monitor.status.no_alerts') : '暂无告警') + '
';
previousAlerts = [];
return;
}
@@ -1483,16 +1483,16 @@ function formatDateTime(timestamp, format = 'relative') {
case 'relative':
// 相对时间格式:刚刚、X分钟前、X小时前、X天前
if (diff < 60) {
- result = typeof i18n !== 'undefined' ? i18n.t('time.just_now') : '刚刚';
+ result = typeof i18n !== 'undefined' ? i18n.t('monitor.time.just_now') : '刚刚';
} else if (diff < 3600) {
const mins = Math.floor(diff / 60);
- result = typeof i18n !== 'undefined' ? i18n.t('time.minutes_ago').replace('{n}', mins) : mins + '分钟前';
+ result = typeof i18n !== 'undefined' ? i18n.t('monitor.time.minutes_ago').replace('{n}', mins) : mins + '分钟前';
} else if (diff < 86400) {
const hours = Math.floor(diff / 3600);
- result = typeof i18n !== 'undefined' ? i18n.t('time.hours_ago').replace('{n}', hours) : hours + '小时前';
+ result = typeof i18n !== 'undefined' ? i18n.t('monitor.time.hours_ago').replace('{n}', hours) : hours + '小时前';
} else {
const days = Math.floor(diff / 86400);
- result = typeof i18n !== 'undefined' ? i18n.t('time.days_ago').replace('{n}', days) : days + '天前';
+ result = typeof i18n !== 'undefined' ? i18n.t('monitor.time.days_ago').replace('{n}', days) : days + '天前';
}
break;
@@ -1507,11 +1507,11 @@ function formatDateTime(timestamp, format = 'relative') {
let dayLabel = '';
if (logDate.getTime() === today.getTime()) {
- dayLabel = typeof i18n !== 'undefined' ? i18n.t('date.today') : '今天';
+ dayLabel = typeof i18n !== 'undefined' ? i18n.t('monitor.date.today') : '今天';
} else if (logDate.getTime() === yesterday.getTime()) {
- dayLabel = typeof i18n !== 'undefined' ? i18n.t('date.yesterday') : '昨天';
+ dayLabel = typeof i18n !== 'undefined' ? i18n.t('monitor.date.yesterday') : '昨天';
} else if (logDate.getTime() === dayBeforeYesterday.getTime()) {
- dayLabel = typeof i18n !== 'undefined' ? i18n.t('time.day_before_yesterday') : '前天';
+ dayLabel = typeof i18n !== 'undefined' ? i18n.t('monitor.time.day_before_yesterday') : '前天';
} else {
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
@@ -1534,11 +1534,11 @@ function formatDateTime(timestamp, format = 'relative') {
let dayLabelFull = '';
if (logDateFull.getTime() === todayFull.getTime()) {
- dayLabelFull = typeof i18n !== 'undefined' ? i18n.t('date.today') : '今天';
+ dayLabelFull = typeof i18n !== 'undefined' ? i18n.t('monitor.date.today') : '今天';
} else if (logDateFull.getTime() === yesterdayFull.getTime()) {
- dayLabelFull = typeof i18n !== 'undefined' ? i18n.t('date.yesterday') : '昨天';
+ dayLabelFull = typeof i18n !== 'undefined' ? i18n.t('monitor.date.yesterday') : '昨天';
} else if (logDateFull.getTime() === dayBeforeYesterdayFull.getTime()) {
- dayLabelFull = typeof i18n !== 'undefined' ? i18n.t('time.day_before_yesterday') : '前天';
+ dayLabelFull = typeof i18n !== 'undefined' ? i18n.t('monitor.time.day_before_yesterday') : '前天';
} else {
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
@@ -1620,7 +1620,7 @@ function initRedisCharts() {
data: {
labels: [],
datasets: [{
- label: typeof i18n !== 'undefined' ? i18n.t('chart.used_connections') : '使用连接数',
+ label: typeof i18n !== 'undefined' ? i18n.t('monitor.chart.used_connections') : '使用连接数',
data: [],
borderColor: '#1890ff',
backgroundColor: 'rgba(24, 144, 255, 0.1)',
@@ -1653,7 +1653,7 @@ function initRedisCharts() {
data: {
labels: [],
datasets: [{
- label: typeof i18n !== 'undefined' ? i18n.t('chart.buffer_size_mb') : '缓冲大小 (MB)',
+ label: typeof i18n !== 'undefined' ? i18n.t('monitor.chart.buffer_size_mb') : '缓冲大小 (MB)',
data: [],
borderColor: '#52c41a',
backgroundColor: 'rgba(82, 196, 26, 0.1)',
@@ -1686,14 +1686,14 @@ function updateRedisDisplay(data) {
const redisBadge = document.getElementById('redis-badge');
if (data.healthy) {
- statusIndicator.textContent = typeof i18n !== 'undefined' ? i18n.t('metric.healthy') : '健康';
+ statusIndicator.textContent = typeof i18n !== 'undefined' ? i18n.t('monitor.metric.healthy') : '健康';
statusIndicator.className = 'status healthy';
- redisBadge.textContent = typeof i18n !== 'undefined' ? i18n.t('metric.healthy') : '正常';
+ redisBadge.textContent = typeof i18n !== 'undefined' ? i18n.t('monitor.metric.healthy') : '正常';
redisBadge.className = 'badge healthy';
} else {
- statusIndicator.textContent = typeof i18n !== 'undefined' ? i18n.t('metric.unhealthy') : '异常';
+ statusIndicator.textContent = typeof i18n !== 'undefined' ? i18n.t('monitor.metric.unhealthy') : '异常';
statusIndicator.className = 'status error';
- redisBadge.textContent = typeof i18n !== 'undefined' ? i18n.t('metric.unhealthy') : '异常';
+ redisBadge.textContent = typeof i18n !== 'undefined' ? i18n.t('monitor.metric.unhealthy') : '异常';
redisBadge.className = 'badge error';
}
@@ -1785,7 +1785,7 @@ function updateLogsDisplay(logs) {
const listEl = getElement('log-list');
if (logs.length === 0) {
- listEl.innerHTML = '' + (typeof i18n !== 'undefined' ? i18n.t('status.no_logs') : '暂无日志') + '
';
+ listEl.innerHTML = '' + (typeof i18n !== 'undefined' ? i18n.t('monitor.status.no_logs') : '暂无日志') + '
';
checkAlertConditions();
return;
}
@@ -1793,7 +1793,7 @@ function updateLogsDisplay(logs) {
listEl.innerHTML = logs.map(log => `