fix(i18n): 补全 367 个缺失的 en-US 翻译键 — monitor 命名空间全覆盖
zh-CN/en-US 翻译键差集从 367 → 0,双语完全对齐 (6088/6088 keys)。
修复内容:
- monitor.alertChannel.* (25 keys): 通知渠道管理弹框
- monitor.alertChannels.* (13 keys): 渠道列表 Tab
- monitor.alertHelper.* (19 keys): 告警辅助函数(渠道/严重度/来源/状态)
- monitor.alertRulesTab.* (13 keys): 告警规则 Tab
- monitor.contactDialog.* (18 keys): 联系人弹框
- monitor.contactGroup.* (14 keys): 联系组弹框
- monitor.hostDashboard.* (23 keys): 主机监控仪表盘
- monitor.logPanel.* (58 keys): 日志查询面板(含 logResults 子命名空间)
- monitor.metricCatalog.* (8 keys): 指标目录
- monitor.monitorRule.* (17 keys): 监控规则配置
- monitor.notifyPolicy.* (30 keys): 通知策略
- monitor.promDs.* (17 keys): Prometheus 数据源配置
- monitor.silenceDialog.* (33 keys): 静默规则弹框
- monitor.silenceRules.* (34 keys): 静默规则列表
- monitor.traceMgr.* (35 keys): 链路追踪管理
新增 scripts/i18n-verify-keys.py 差集检测脚本 (--fix 可自动补全占位)。
⚠️ 缺失翻译键是静默失败: i18next 渲染 key 路径本身而非翻译文本,
切换到英文时用户看到 monitor.xxx.xxx 而非英文。
This commit is contained in:
1 parent
55f216cfbb
commit
eb98a95842
2 files changed
+493
-1
No files matched your search
@@ -3262,7 +3262,406 @@
|
|||||||
"prometheusdashb.sb0f653eb": "内存 Top 10",
|
"prometheusdashb.sb0f653eb": "内存 Top 10",
|
||||||
"prometheusdashb.s5e8eb212": "连接中…",
|
"prometheusdashb.s5e8eb212": "连接中…",
|
||||||
"zabbixdashboard.sdb131ab4": "未找到被监控的 Zabbix 主机",
|
"zabbixdashboard.sdb131ab4": "未找到被监控的 Zabbix 主机",
|
||||||
"zabbixdashboard.s5dc99f6e": "问题"
|
"zabbixdashboard.s5dc99f6e": "问题",
|
||||||
|
"alertChannel": {
|
||||||
|
"addBtn": "Add Channel",
|
||||||
|
"addTitle": "Add Notification Channel",
|
||||||
|
"added": "Channel added",
|
||||||
|
"configJson": "Config (JSON)",
|
||||||
|
"dialogDesc": "Configure alert notification channels (DingTalk/WeChat/Email/SMS/Webhook)",
|
||||||
|
"editTitle": "Edit Notification Channel",
|
||||||
|
"hintDingtalk": "DingTalk bot: webhook URL + secret",
|
||||||
|
"hintEmail": "Email: {\"to\":\"ops@ezops.io\"}",
|
||||||
|
"hintFeishu": "Feishu bot: {\"url\":\"https://open.feishu.cn/open-apis/bot/v2/hook/...\"}",
|
||||||
|
"hintSms": "SMS: {\"phones\":\"13800138000\"}",
|
||||||
|
"hintWebhook": "Webhook: {\"url\":\"https://...\"}",
|
||||||
|
"hintWechat": "WeCom bot: webhook URL",
|
||||||
|
"namePlaceholder": "e.g., Ops Alert Group",
|
||||||
|
"nameRequired": "Channel Name *",
|
||||||
|
"saveChanges": "Save Changes",
|
||||||
|
"saving": "Saving...",
|
||||||
|
"typeDingtalk": "DingTalk",
|
||||||
|
"typeEmail": "Email",
|
||||||
|
"typeFeishu": "Feishu",
|
||||||
|
"typeLabel": "Channel Type",
|
||||||
|
"typeSms": "SMS",
|
||||||
|
"typeWebhook": "Webhook",
|
||||||
|
"typeWechat": "WeCom",
|
||||||
|
"updated": "Channel updated"
|
||||||
|
},
|
||||||
|
"alertChannels": {
|
||||||
|
"addChannel": "Add Channel",
|
||||||
|
"deleteConfirm": "Delete this notification channel?",
|
||||||
|
"deleteFailed": "Delete failed",
|
||||||
|
"deleteTitle": "Delete Notification Channel",
|
||||||
|
"deleted": "Channel deleted",
|
||||||
|
"disabled": "Disabled",
|
||||||
|
"empty": "No notification channels yet. Click \"Add Channel\" to create.",
|
||||||
|
"fetchFailed": "Failed to fetch notification channels",
|
||||||
|
"testFailed": "Test failed",
|
||||||
|
"testNoConfig": "Channel not configured. Please complete config first.",
|
||||||
|
"testPassed": "Test passed",
|
||||||
|
"testSent": "Test notification sent",
|
||||||
|
"typeLabel": "Type"
|
||||||
|
},
|
||||||
|
"alertHelper": {
|
||||||
|
"alertIdFallback": "Alert #{{id}}",
|
||||||
|
"chDingtalk": "DingTalk",
|
||||||
|
"chEmail": "Email",
|
||||||
|
"chFeishu": "Feishu",
|
||||||
|
"chSms": "SMS",
|
||||||
|
"chWebhook": "Webhook",
|
||||||
|
"chWechat": "WeCom",
|
||||||
|
"sevCritical": "Critical",
|
||||||
|
"sevInfo": "Info",
|
||||||
|
"sevWarning": "Warning",
|
||||||
|
"srcHost": "Host Monitor",
|
||||||
|
"srcLog": "Log Alert",
|
||||||
|
"srcMiddleware": "Middleware Monitor",
|
||||||
|
"srcSite": "Site Probe",
|
||||||
|
"srcSnmp": "SNMP Monitor",
|
||||||
|
"statusAcknowledged": "Acknowledged",
|
||||||
|
"statusFiring": "Firing",
|
||||||
|
"statusResolved": "Resolved",
|
||||||
|
"statusSuppressed": "Suppressed"
|
||||||
|
},
|
||||||
|
"alertRulesTab": {
|
||||||
|
"addRule": "Add Rule",
|
||||||
|
"catHost": "Host Monitoring",
|
||||||
|
"catHostDesc": "Agent CPU/Memory/Disk/Offline",
|
||||||
|
"catMiddleware": "Middleware",
|
||||||
|
"catMiddlewareDesc": "Redis / MySQL metrics",
|
||||||
|
"catSite": "Site Probe",
|
||||||
|
"catSiteDesc": "HTTP/HTTPS availability & certificate",
|
||||||
|
"catSnmp": "SNMP",
|
||||||
|
"catSnmpDesc": "Network devices / BMC",
|
||||||
|
"defaultRules": "Default Rules",
|
||||||
|
"disabled": "Disabled",
|
||||||
|
"emptyDesc": "Click \"Add Rule\" or \"Default Rules\" to create",
|
||||||
|
"emptyTitle": "No {{cat}} rules",
|
||||||
|
"loading": "Loading rules..."
|
||||||
|
},
|
||||||
|
"contactDialog": {
|
||||||
|
"addContactBtn": "Add Contact",
|
||||||
|
"addTitle": "Add Alert Contact",
|
||||||
|
"channelPlaceholder": "Select channel type",
|
||||||
|
"channelTypeRequired": "Channel Type *",
|
||||||
|
"contactAdded": "Contact added",
|
||||||
|
"contactUpdated": "Contact updated",
|
||||||
|
"customWebhook": "Custom Webhook",
|
||||||
|
"dialogDesc": "Select notification channel and enter the Webhook URL",
|
||||||
|
"editTitle": "Edit Alert Contact",
|
||||||
|
"emailLabel": "Email",
|
||||||
|
"feishu": "Feishu",
|
||||||
|
"namePlaceholder": "e.g., John Doe",
|
||||||
|
"nameRequired": "Name *",
|
||||||
|
"phoneLabel": "Phone",
|
||||||
|
"saveChanges": "Save Changes",
|
||||||
|
"saving": "Saving...",
|
||||||
|
"webhookRequired": "Webhook URL *"
|
||||||
|
},
|
||||||
|
"contactGroup": {
|
||||||
|
"addBtn": "Add Contact Group",
|
||||||
|
"addTitle": "Add Alert Contact Group",
|
||||||
|
"added": "Contact group added",
|
||||||
|
"dialogDesc": "Configure alert contact group and select members",
|
||||||
|
"editTitle": "Edit Alert Contact Group",
|
||||||
|
"namePlaceholder": "e.g., Ops On-Call Team",
|
||||||
|
"nameRequired": "Group Name *",
|
||||||
|
"noContacts": "No contacts. Please add in \"Alert Contacts\" tab first.",
|
||||||
|
"notes": "Notes",
|
||||||
|
"notesPlaceholder": "e.g., Mon-Fri 9:00-21:00",
|
||||||
|
"saveChanges": "Save Changes",
|
||||||
|
"saving": "Saving...",
|
||||||
|
"selectContacts": "Select Contacts",
|
||||||
|
"selectedCount": "{{count}} contacts selected",
|
||||||
|
"updated": "Contact group updated"
|
||||||
|
},
|
||||||
|
"hostDashboard": {
|
||||||
|
"agentOnline": "Agent Online",
|
||||||
|
"alert": "Alert",
|
||||||
|
"avgCpu": "Avg CPU",
|
||||||
|
"avgMemory": "Avg Memory",
|
||||||
|
"colAgentStatus": "Agent Status",
|
||||||
|
"colAgentVersion": "Agent Version",
|
||||||
|
"colHostId": "Hostname/ID",
|
||||||
|
"colIp": "IP Address",
|
||||||
|
"colRegion": "Region",
|
||||||
|
"cpuTop5": "CPU Usage Top 5",
|
||||||
|
"hostTotal": "Total Hosts",
|
||||||
|
"listTitle": "Host Monitoring List",
|
||||||
|
"memTop5": "Memory Usage Top 5",
|
||||||
|
"monitor": "Monitor",
|
||||||
|
"noData": "No host data",
|
||||||
|
"noDataDesc": "Please add a server first or wait for Agent data",
|
||||||
|
"perPage": "Per Page",
|
||||||
|
"realtime": "Realtime",
|
||||||
|
"searchPlaceholder": "Search hostname/IP...",
|
||||||
|
"threshold": "Threshold: 60% / 85%",
|
||||||
|
"totalCount": "items, {{count}} total",
|
||||||
|
"viewAlertRules": "View Alert Rules",
|
||||||
|
"viewMonitorChart": "View Monitor Charts"
|
||||||
|
},
|
||||||
|
"logPanel": {
|
||||||
|
"apiNotFound": "API not found",
|
||||||
|
"authFailed": "Authentication failed",
|
||||||
|
"chartCountLabel": "Count",
|
||||||
|
"chartCountSuffix": "log entries",
|
||||||
|
"chartTimePrefix": "Time:",
|
||||||
|
"connFailed": "Connection failed",
|
||||||
|
"copied": "Copied",
|
||||||
|
"copiedDesc": "{{count}} log entries on current page",
|
||||||
|
"copyFailed": "Copy failed",
|
||||||
|
"copyFailedDesc": "Please use export or expand individual entries",
|
||||||
|
"custom": "Custom",
|
||||||
|
"defaultSuffix": " (default)",
|
||||||
|
"emptyHint": "Enter a query and click \"Query\" to view results",
|
||||||
|
"exported": "Exported",
|
||||||
|
"exportedDesc": "{{count}} log entries",
|
||||||
|
"history": "Query History",
|
||||||
|
"last5min": "Last 5 minutes",
|
||||||
|
"limit": "Limit",
|
||||||
|
"liveTailTitle": "Live Tail - refreshes last 5 min every 3 seconds",
|
||||||
|
"loadMoreFailed": "Load more failed",
|
||||||
|
"logResults": {
|
||||||
|
"addLabelFilter": "Add to query",
|
||||||
|
"collapseAll": "Collapse All",
|
||||||
|
"commonLabels": "Common Labels",
|
||||||
|
"content": "Content",
|
||||||
|
"contextTitle": "Context",
|
||||||
|
"copiedFullLog": "Full log copied",
|
||||||
|
"copyAll": "Copy All",
|
||||||
|
"copyFailedExpandDesc": "Please expand and manually select to copy",
|
||||||
|
"copyFullLog": "Copy Full Log",
|
||||||
|
"copyLine": "Copy",
|
||||||
|
"copyLineDesc": "Copy this line",
|
||||||
|
"excludeLabelFilter": "Exclude this value",
|
||||||
|
"expandAll": "Expand All",
|
||||||
|
"fullLog": "Full Log",
|
||||||
|
"hideLabels": "Hide Labels",
|
||||||
|
"jsonFormat": "JSON Format",
|
||||||
|
"labels": "Labels",
|
||||||
|
"level": "Level",
|
||||||
|
"lineNo": "Line No.",
|
||||||
|
"loading": "Loading...",
|
||||||
|
"noLabels": "No Labels",
|
||||||
|
"noLogs": "No Logs",
|
||||||
|
"noResults": "No log data",
|
||||||
|
"raw": "Raw",
|
||||||
|
"showLabels": "Show Labels",
|
||||||
|
"source": "Source",
|
||||||
|
"time": "Time",
|
||||||
|
"viewContext": "View Context"
|
||||||
|
},
|
||||||
|
"noDatasource": "No {{name}} datasource available",
|
||||||
|
"noDatasourceHint": "No {{name}} datasource. Please add one in \"Log Library\" first.",
|
||||||
|
"noMatch": "No matching log entries found",
|
||||||
|
"noMatchDesc": "Query executed ({{ms}}ms), no results match current filters",
|
||||||
|
"query": "Query",
|
||||||
|
"queryFailed": "Query failed",
|
||||||
|
"requeries": "Retry Query",
|
||||||
|
"saveAs": "Save As",
|
||||||
|
"saveAsAlertItem": "Alert",
|
||||||
|
"saveAsQueryItem": "Query",
|
||||||
|
"savedQueries": "Saved Queries",
|
||||||
|
"share": "Share",
|
||||||
|
"shareCopied": "Share link copied",
|
||||||
|
"shareCopiedDesc": "Recipients open the link to auto-execute the query",
|
||||||
|
"shareCopyFailed": "Copy failed",
|
||||||
|
"shareLinkTitle": "Copy Share Link",
|
||||||
|
"tabCluster": "Log Clusters",
|
||||||
|
"tabLogs": "Raw Logs",
|
||||||
|
"tabStats": "Statistics",
|
||||||
|
"truncate": "Truncate Preview",
|
||||||
|
"truncateTitle": "Collapsed rows show only first 500 characters",
|
||||||
|
"viewField": "Field View",
|
||||||
|
"viewRaw": "Raw Logs",
|
||||||
|
"viewTable": "Table View",
|
||||||
|
"wrapLines": "Wrap Lines",
|
||||||
|
"wrapLinesTitle": "Auto-wrap long log entries"
|
||||||
|
},
|
||||||
|
"metricCatalog": {
|
||||||
|
"colCrit": "Critical Threshold",
|
||||||
|
"colDesc": "Description",
|
||||||
|
"colMax": "Max",
|
||||||
|
"colMetric": "Metric",
|
||||||
|
"colType": "Type",
|
||||||
|
"colUnit": "Unit",
|
||||||
|
"colWarn": "Warning Threshold",
|
||||||
|
"desc": "Collectible metrics for Host / Middleware / SNMP / Site Probe, with default alert thresholds"
|
||||||
|
},
|
||||||
|
"monitorRule": {
|
||||||
|
"catMiddleware": "Middleware",
|
||||||
|
"catSite": "Site Probe",
|
||||||
|
"dialogDesc": "Configure threshold alerts based on monitoring metrics",
|
||||||
|
"editRuleTitle": "Edit {{cat}} Alert Rule",
|
||||||
|
"enableRule": "Enable Rule",
|
||||||
|
"metricLabel": "Metric",
|
||||||
|
"nameMetricRequired": "Please fill in rule name and metric",
|
||||||
|
"namePlaceholder": "e.g., Redis connection count too high",
|
||||||
|
"newRuleTitle": "New {{cat}} Alert Rule",
|
||||||
|
"opEq": "= Equal to",
|
||||||
|
"opGt": "> Greater than",
|
||||||
|
"opGte": ">= Greater or equal",
|
||||||
|
"opLt": "< Less than",
|
||||||
|
"opLte": "<= Less or equal",
|
||||||
|
"ruleCreated": "Rule created",
|
||||||
|
"ruleUpdated": "Rule updated",
|
||||||
|
"thresholdLabel": "Threshold"
|
||||||
|
},
|
||||||
|
"notifyPolicy": {
|
||||||
|
"cardFooter": "Card Footer",
|
||||||
|
"catHost": "Host Monitoring",
|
||||||
|
"catHostDesc": "Agent CPU/Memory/Disk/Offline",
|
||||||
|
"catMiddleware": "Middleware Monitoring",
|
||||||
|
"catMiddlewareDesc": "Redis/MySQL metrics",
|
||||||
|
"catSite": "Site Probe",
|
||||||
|
"catSiteDesc": "HTTP/TCP/DNS probe failures and latency",
|
||||||
|
"catSnmp": "SNMP Monitoring",
|
||||||
|
"catSnmpDesc": "H3C/Huawei/Cisco/Ruijie/Dell/Inspur",
|
||||||
|
"disable": "Disable",
|
||||||
|
"extraContacts": "Extra Contacts",
|
||||||
|
"fallbackHint": "If unconfigured, falls back to global Feishu channel in \"Notification Channels\".",
|
||||||
|
"loadFailed": "Failed to load notification policies",
|
||||||
|
"loading": "Loading notification policies...",
|
||||||
|
"msgTemplateTitle": "Message Template",
|
||||||
|
"noContacts": "No contacts",
|
||||||
|
"noGroups": "No contact groups",
|
||||||
|
"noTargetHint": "Please create targets in \"Alert Contacts\" and \"Contact Groups\" first.",
|
||||||
|
"notifyGroups": "Notify Groups",
|
||||||
|
"platformName": "Platform Signature",
|
||||||
|
"preview": "Preview",
|
||||||
|
"saveAll": "Save All",
|
||||||
|
"saved": "Notification config saved",
|
||||||
|
"selectedSummary": "{{groups}} groups + {{contacts}} contacts selected",
|
||||||
|
"testFailed": "Test failed. Check policy and contacts.",
|
||||||
|
"testSent": "Test notification sent",
|
||||||
|
"titleTemplate": "Title Template",
|
||||||
|
"topDesc": "Configure default notification targets by monitoring type. Templates apply to Feishu card titles and footer.",
|
||||||
|
"unsaved": "Unsaved"
|
||||||
|
},
|
||||||
|
"promDs": {
|
||||||
|
"caCertPlaceholder": "PEM format CA certificate content...",
|
||||||
|
"configDesc": "Prometheus datasource config (similar to Grafana)",
|
||||||
|
"createSuccess": "Datasource created",
|
||||||
|
"namePlaceholder": "Prometheus datasource name",
|
||||||
|
"nameRequired": "Please enter a datasource name",
|
||||||
|
"noHeaders": "No custom headers",
|
||||||
|
"opFailed": "Operation failed",
|
||||||
|
"password": "Password",
|
||||||
|
"queryParamsPlaceholder": "e.g.: {\"timeout\": \"30s\"}",
|
||||||
|
"saveTest": "Save & Test",
|
||||||
|
"second": "seconds",
|
||||||
|
"testFailed": "Test failed",
|
||||||
|
"updateSuccess": "Datasource updated",
|
||||||
|
"urlInputRequired": "Please enter a URL first",
|
||||||
|
"urlRequired": "Please enter a Prometheus URL",
|
||||||
|
"username": "Username"
|
||||||
|
},
|
||||||
|
"silenceDialog": {
|
||||||
|
"createTitle": "Create Silence Rule",
|
||||||
|
"dailyEnd": "Daily End",
|
||||||
|
"dailyStart": "Daily Start",
|
||||||
|
"descPlaceholder": "Optional notes",
|
||||||
|
"dialogDesc": "Alerts matching silence rules will not trigger notifications during silence period",
|
||||||
|
"editTitle": "Edit Silence Rule",
|
||||||
|
"effectiveWeekdays": "Effective Weekdays",
|
||||||
|
"endTime": "End Time",
|
||||||
|
"endTimeRequired": "Please set an end time",
|
||||||
|
"fri": "Fri",
|
||||||
|
"hintDaily": "Silence alerts during specified hours daily. Good for off-hours do-not-disturb.",
|
||||||
|
"hintOnetime": "Matching alerts silenced within the time window. Resumes automatically after window ends.",
|
||||||
|
"hintWeekly": "Silence only on selected weekdays during specified hours. Good for weekend maintenance.",
|
||||||
|
"mon": "Mon",
|
||||||
|
"namePlaceholder": "e.g., Maintenance Window Silence",
|
||||||
|
"nameRequired": "Please enter a rule name",
|
||||||
|
"ruleNameRequired": "Rule Name *",
|
||||||
|
"sat": "Sat",
|
||||||
|
"saving": "Saving...",
|
||||||
|
"silenceCreated": "Silence rule created",
|
||||||
|
"silenceType": "Silence Type",
|
||||||
|
"silenceUpdated": "Silence rule updated",
|
||||||
|
"startTime": "Start Time",
|
||||||
|
"sun": "Sun",
|
||||||
|
"thu": "Thu",
|
||||||
|
"tue": "Tue",
|
||||||
|
"typeDaily": "Daily Recurring",
|
||||||
|
"typeOnetime": "One-time",
|
||||||
|
"typeWeekly": "Weekly Recurring",
|
||||||
|
"wed": "Wed",
|
||||||
|
"weekdayRequired": "Weekly silence requires at least one weekday"
|
||||||
|
},
|
||||||
|
"silenceRules": {
|
||||||
|
"active": "Active",
|
||||||
|
"addRule": "Add Silence Rule",
|
||||||
|
"createdAt": "Created {{time}}",
|
||||||
|
"dailyPrefix": "Daily",
|
||||||
|
"dayFri": "F",
|
||||||
|
"dayMon": "M",
|
||||||
|
"daySat": "S",
|
||||||
|
"daySun": "S",
|
||||||
|
"dayThu": "T",
|
||||||
|
"dayTue": "T",
|
||||||
|
"dayWed": "W",
|
||||||
|
"deleteConfirm": "Delete \"{{name}}\"?",
|
||||||
|
"deleteConfirmRestore": "Delete \"{{name}}\"? Alerts will resume after deletion.",
|
||||||
|
"deleteFailed": "Delete failed",
|
||||||
|
"deleteTitle": "Delete Silence Rule",
|
||||||
|
"deleted": "Deleted",
|
||||||
|
"disabled": "Disabled",
|
||||||
|
"disabledBadge": "Disabled",
|
||||||
|
"empty": "No silence rules. Alerts sent per notification policy.",
|
||||||
|
"emptyHint": "Click \"Add Silence Rule\" to create a maintenance window",
|
||||||
|
"emptyShort": "No silence rules",
|
||||||
|
"enabled": "Enabled",
|
||||||
|
"fetchFailed": "Failed to fetch silence rules",
|
||||||
|
"subtitle": "Maintenance windows / Do-not-disturb",
|
||||||
|
"title": "Silence Rules",
|
||||||
|
"totalCount": "{{count}} rules total",
|
||||||
|
"typeDaily": "Daily",
|
||||||
|
"typeOnetime": "One-time",
|
||||||
|
"typeWeekly": "Weekly",
|
||||||
|
"weekPrefix": "Wk"
|
||||||
|
},
|
||||||
|
"traceMgr": {
|
||||||
|
"allServices": "All Services",
|
||||||
|
"avgLatency": "Avg Latency",
|
||||||
|
"colServiceOperation": "Service / Operation",
|
||||||
|
"day7": "7 days",
|
||||||
|
"errorRate": "Error Rate",
|
||||||
|
"errorTrend": "Error Rate Trend",
|
||||||
|
"errorTrendChart": "Error rate trend chart (shown after chart library integration)",
|
||||||
|
"fetchDetailFailed": "Failed to fetch trace detail",
|
||||||
|
"fetchServicesFailed": "Failed to fetch service data",
|
||||||
|
"fetchTracesFailed": "Failed to fetch trace data",
|
||||||
|
"hour1": "1 hour",
|
||||||
|
"hour24": "24 hours",
|
||||||
|
"hour6": "6 hours",
|
||||||
|
"inProgress": "In Progress",
|
||||||
|
"latencyDist": "Latency Distribution",
|
||||||
|
"latencyDistChart": "Latency distribution chart (shown after chart library integration)",
|
||||||
|
"loadingTraces": "Loading trace data...",
|
||||||
|
"logs": "Logs",
|
||||||
|
"min15": "15 minutes",
|
||||||
|
"noTraces": "No trace data",
|
||||||
|
"p95Latency": "P95 Latency",
|
||||||
|
"p99Latency": "P99 Latency",
|
||||||
|
"requestCount": "Requests",
|
||||||
|
"searchPlaceholder": "Search Trace ID, operation name...",
|
||||||
|
"serviceCount": "Services",
|
||||||
|
"serviceName": "Service Name",
|
||||||
|
"servicePerfTitle": "Service Performance Overview",
|
||||||
|
"spanCount": "Spans",
|
||||||
|
"spanDetailTitle": "Span Detail",
|
||||||
|
"spanName": "Span Name",
|
||||||
|
"subtitle": "Distributed trace query and service performance analysis",
|
||||||
|
"tabMetrics": "Performance Metrics",
|
||||||
|
"tabServices": "Service Topology",
|
||||||
|
"tabTraces": "Traces",
|
||||||
|
"totalDuration": "Total Duration",
|
||||||
|
"totalRequests": "Total Requests",
|
||||||
|
"traceDetailTitle": "Trace Detail"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"task": {
|
"task": {
|
||||||
"cronJob": {
|
"cronJob": {
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
i18n-verify-keys.py — Detect missing/extra translation keys between zh-CN and en-US.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python3 scripts/i18n-verify-keys.py # Report only
|
||||||
|
python3 scripts/i18n-verify-keys.py --fix # Copy zh-CN values as placeholder
|
||||||
|
|
||||||
|
Exit code: 0 if keys are in sync, 1 if there are missing keys.
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
def flatten(d, prefix=''):
|
||||||
|
items = {}
|
||||||
|
for k, v in d.items():
|
||||||
|
full = f'{prefix}.{k}' if prefix else k
|
||||||
|
if isinstance(v, dict):
|
||||||
|
items.update(flatten(v, full))
|
||||||
|
else:
|
||||||
|
items[full] = v
|
||||||
|
return items
|
||||||
|
|
||||||
|
def set_nested(d, key_path, value):
|
||||||
|
parts = key_path.split('.')
|
||||||
|
current = d
|
||||||
|
for part in parts[:-1]:
|
||||||
|
if part not in current or not isinstance(current[part], dict):
|
||||||
|
current[part] = {}
|
||||||
|
current = current[part]
|
||||||
|
current[parts[-1]] = value
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description='Verify i18n key parity between zh-CN and en-US')
|
||||||
|
parser.add_argument('--fix', action='store_true', help='Copy zh-CN values as placeholder for missing en-US keys')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
base = os.path.join(os.path.dirname(__file__), '..', 'frontend', 'src', 'locales')
|
||||||
|
zh_path = os.path.join(base, 'zh-CN.json')
|
||||||
|
en_path = os.path.join(base, 'en-US.json')
|
||||||
|
|
||||||
|
zh = json.load(open(zh_path))
|
||||||
|
en = json.load(open(en_path))
|
||||||
|
|
||||||
|
zh_flat = flatten(zh)
|
||||||
|
en_flat = flatten(en)
|
||||||
|
|
||||||
|
missing_en = sorted(set(zh_flat) - set(en_flat))
|
||||||
|
extra_en = sorted(set(en_flat) - set(zh_flat))
|
||||||
|
|
||||||
|
print(f'zh-CN keys: {len(zh_flat)}')
|
||||||
|
print(f'en-US keys: {len(en_flat)}')
|
||||||
|
print(f'Missing in en-US: {len(missing_en)}')
|
||||||
|
print(f'Extra in en-US: {len(extra_en)}')
|
||||||
|
|
||||||
|
if missing_en:
|
||||||
|
# Group by namespace
|
||||||
|
ns = {}
|
||||||
|
for k in missing_en:
|
||||||
|
prefix = k.split('.')[0] + '.' + k.split('.')[1] if '.' in k else k
|
||||||
|
ns[prefix] = ns.get(prefix, 0) + 1
|
||||||
|
print('\nMissing by sub-namespace:')
|
||||||
|
for k, v in sorted(ns.items()):
|
||||||
|
print(f' {k}.* : {v} keys')
|
||||||
|
|
||||||
|
print('\nFirst 20 missing keys:')
|
||||||
|
for k in missing_en[:20]:
|
||||||
|
print(f' - {k} = {zh_flat[k]}')
|
||||||
|
|
||||||
|
if extra_en:
|
||||||
|
print('\nExtra en-US keys (not in zh-CN):')
|
||||||
|
for k in extra_en[:10]:
|
||||||
|
print(f' + {k}')
|
||||||
|
|
||||||
|
if args.fix and missing_en:
|
||||||
|
for key in missing_en:
|
||||||
|
set_nested(en, key, zh_flat[key])
|
||||||
|
with open(en_path, 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(en, f, ensure_ascii=False, indent=2)
|
||||||
|
f.write('\n')
|
||||||
|
print(f'\n[fix] Copied {len(missing_en)} zh-CN values as en-US placeholders.')
|
||||||
|
print('[fix] NOTE: Replace with proper English translations!')
|
||||||
|
|
||||||
|
if missing_en or extra_en:
|
||||||
|
sys.exit(1)
|
||||||
|
else:
|
||||||
|
print('\n✅ zh-CN and en-US keys are in sync.')
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
Reference in new issue
Block a user