From 71f6a97a90cfda13261a71a38cba549146f0a505 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=85=AC=E6=98=8E?= <83812544+Ed1s0nZ@users.noreply.github.com> Date: Mon, 9 Mar 2026 23:00:24 +0800 Subject: [PATCH] Add files via upload --- web/static/i18n/en-US.json | 9 ++++++++- web/static/i18n/zh-CN.json | 9 ++++++++- web/static/js/chat.js | 21 ++++++++++++++------- web/static/js/i18n.js | 6 ++++++ web/static/js/monitor.js | 30 ++++++++++++++++++------------ 5 files changed, 54 insertions(+), 21 deletions(-) diff --git a/web/static/i18n/en-US.json b/web/static/i18n/en-US.json index 38105db0..9c042d07 100644 --- a/web/static/i18n/en-US.json +++ b/web/static/i18n/en-US.json @@ -158,7 +158,10 @@ "searching": "Searching...", "loadFailedRetry": "Load failed, please retry", "dataFormatError": "Data format error", - "progressInProgress": "Penetration test in progress..." + "progressInProgress": "Penetration test in progress...", + "executionFailed": "Execution failed", + "penetrationTestComplete": "Penetration test complete", + "yesterday": "Yesterday" }, "progress": { "callingAI": "Calling AI model...", @@ -200,6 +203,10 @@ "unknownTime": "Unknown time", "clearHistoryConfirm": "Clear all task history?", "cancelTaskFailed": "Cancel task failed", + "cancelFailed": "Cancel failed", + "taskInfoNotSynced": "Task info not synced yet, please try again later.", + "loadActiveTasksFailed": "Failed to load active tasks", + "cannotGetTaskStatus": "Cannot get task status", "copiedToast": "Copied!", "cancelling": "Cancelling...", "enterTaskPrompt": "Enter at least one task", diff --git a/web/static/i18n/zh-CN.json b/web/static/i18n/zh-CN.json index 637db159..90fd5293 100644 --- a/web/static/i18n/zh-CN.json +++ b/web/static/i18n/zh-CN.json @@ -158,7 +158,10 @@ "searching": "搜索中...", "loadFailedRetry": "加载失败,请重试", "dataFormatError": "数据格式错误", - "progressInProgress": "渗透测试进行中..." + "progressInProgress": "渗透测试进行中...", + "executionFailed": "执行失败", + "penetrationTestComplete": "渗透测试完成", + "yesterday": "昨天" }, "progress": { "callingAI": "正在调用AI模型...", @@ -200,6 +203,10 @@ "unknownTime": "未知时间", "clearHistoryConfirm": "确定要清空所有任务历史记录吗?", "cancelTaskFailed": "取消任务失败", + "cancelFailed": "取消失败", + "taskInfoNotSynced": "任务信息尚未同步,请稍后再试。", + "loadActiveTasksFailed": "获取活跃任务失败", + "cannotGetTaskStatus": "无法获取任务状态", "copiedToast": "已复制!", "cancelling": "取消中...", "enterTaskPrompt": "请输入至少一个任务", diff --git a/web/static/js/chat.js b/web/static/js/chat.js index 4f0d8755..9594d5a4 100644 --- a/web/static/js/chat.js +++ b/web/static/js/chat.js @@ -1188,7 +1188,8 @@ function addMessage(role, content, mcpExecutionIds = null, progressId = null, cr } else { messageTime = new Date(); } - timeDiv.textContent = messageTime.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' }); + const msgTimeLocale = (typeof window.__locale === 'string' && window.__locale.startsWith('zh')) ? 'zh-CN' : 'en-US'; + timeDiv.textContent = messageTime.toLocaleTimeString(msgTimeLocale, { hour: '2-digit', minute: '2-digit' }); contentWrapper.appendChild(timeDiv); // 如果有MCP执行ID或进度ID,添加查看详情区域(统一使用"渗透测试详情"样式) @@ -1433,6 +1434,8 @@ function renderProcessDetails(messageId, processDetails) { itemTitle = '❌ ' + (typeof window.t === 'function' ? window.t('chat.error') : '错误'); } else if (eventType === 'cancelled') { itemTitle = '⛔ ' + (typeof window.t === 'function' ? window.t('chat.taskCancelled') : '任务已取消'); + } else if (eventType === 'progress') { + itemTitle = typeof window.translateProgressMessage === 'function' ? window.translateProgressMessage(detail.message || '') : (detail.message || ''); } addTimelineItem(timeline, eventType, { @@ -1533,8 +1536,9 @@ async function showMCPDetail(executionId) { const normalizedStatus = (exec.status || 'unknown').toLowerCase(); statusEl.textContent = getStatusText(exec.status); statusEl.className = `status-chip status-${normalizedStatus}`; + const detailTimeLocale = (typeof window.__locale === 'string' && window.__locale.startsWith('zh')) ? 'zh-CN' : 'en-US'; document.getElementById('detail-time').textContent = exec.startTime - ? new Date(exec.startTime).toLocaleString('zh-CN') + ? new Date(exec.startTime).toLocaleString(detailTimeLocale) : '—'; // 请求参数 @@ -1950,28 +1954,30 @@ function formatConversationTimestamp(dateObj, todayStart, yesterdayStart) { const referenceToday = todayStart || new Date(now.getFullYear(), now.getMonth(), now.getDate()); const referenceYesterday = yesterdayStart || new Date(referenceToday.getTime() - 24 * 60 * 60 * 1000); const messageDate = new Date(dateObj.getFullYear(), dateObj.getMonth(), dateObj.getDate()); + const fmtLocale = (typeof window.__locale === 'string' && window.__locale.startsWith('zh')) ? 'zh-CN' : 'en-US'; + const yesterdayLabel = typeof window.t === 'function' ? window.t('chat.yesterday') : '昨天'; if (messageDate.getTime() === referenceToday.getTime()) { - return dateObj.toLocaleTimeString('zh-CN', { + return dateObj.toLocaleTimeString(fmtLocale, { hour: '2-digit', minute: '2-digit' }); } if (messageDate.getTime() === referenceYesterday.getTime()) { - return '昨天 ' + dateObj.toLocaleTimeString('zh-CN', { + return yesterdayLabel + ' ' + dateObj.toLocaleTimeString(fmtLocale, { hour: '2-digit', minute: '2-digit' }); } if (dateObj.getFullYear() === referenceToday.getFullYear()) { - return dateObj.toLocaleString('zh-CN', { + return dateObj.toLocaleString(fmtLocale, { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }); } - return dateObj.toLocaleString('zh-CN', { + return dateObj.toLocaleString(fmtLocale, { year: 'numeric', month: 'short', day: 'numeric', @@ -5663,7 +5669,8 @@ async function loadGroupConversations(groupId, searchQuery = '') { const timeWrapper = document.createElement('div'); timeWrapper.className = 'group-conversation-time'; const dateObj = fullConv.updatedAt ? new Date(fullConv.updatedAt) : new Date(); - timeWrapper.textContent = dateObj.toLocaleString('zh-CN', { + const convListLocale = (typeof window.__locale === 'string' && window.__locale.startsWith('zh')) ? 'zh-CN' : 'en-US'; + timeWrapper.textContent = dateObj.toLocaleString(convListLocale, { year: 'numeric', month: 'long', day: 'numeric', diff --git a/web/static/js/i18n.js b/web/static/js/i18n.js index 6e7b117f..a5fbf803 100644 --- a/web/static/js/i18n.js +++ b/web/static/js/i18n.js @@ -148,6 +148,9 @@ } applyTranslations(document); updateLangLabel(); + try { + window.__locale = lang; + } catch (e) { /* ignore */ } try { document.dispatchEvent(new CustomEvent('languagechange', { detail: { lang: lang } })); } catch (e) { /* ignore */ } @@ -170,6 +173,9 @@ await loadLanguageResources(initialLang); applyTranslations(document); updateLangLabel(); + try { + window.__locale = i18next.language || initialLang; + } catch (e) { /* ignore */ } // 导出全局函数供其他脚本调用(支持插值参数,如 _t('key', { count: 2 })) window.t = function (key, opts) { diff --git a/web/static/js/monitor.js b/web/static/js/monitor.js index 8a68c46d..13bbb89b 100644 --- a/web/static/js/monitor.js +++ b/web/static/js/monitor.js @@ -23,6 +23,9 @@ function translateProgressMessage(message) { } return message; } +if (typeof window !== 'undefined') { + window.translateProgressMessage = translateProgressMessage; +} // 存储工具调用ID到DOM元素的映射,用于更新执行状态 const toolCallStatusMap = new Map(); @@ -101,7 +104,7 @@ async function requestCancel(conversationId) { }); const result = await response.json().catch(() => ({})); if (!response.ok) { - throw new Error(result.error || '取消失败'); + throw new Error(result.error || (typeof window.t === 'function' ? window.t('tasks.cancelFailed') : '取消失败')); } return result; } @@ -186,7 +189,7 @@ function collapseAllProgressDetails(assistantMessageId, progressId) { if (timeline) { timeline.classList.remove('expanded'); if (toggleBtn) { - toggleBtn.textContent = '展开详情'; + toggleBtn.textContent = typeof window.t === 'function' ? window.t('chat.expandDetail') : '展开详情'; } } }); @@ -198,7 +201,7 @@ function collapseAllProgressDetails(assistantMessageId, progressId) { if (progressTimeline) { progressTimeline.classList.remove('expanded'); if (progressToggleBtn) { - progressToggleBtn.textContent = '展开详情'; + progressToggleBtn.textContent = typeof window.t === 'function' ? window.t('chat.expandDetail') : '展开详情'; } } } @@ -347,7 +350,7 @@ async function cancelProgressTask(progressId) { stopBtn.disabled = false; }, 1500); } - alert('任务信息尚未同步,请稍后再试。'); + alert(typeof window.t === 'function' ? window.t('tasks.taskInfoNotSynced') : '任务信息尚未同步,请稍后再试。'); return; } @@ -358,7 +361,7 @@ async function cancelProgressTask(progressId) { markProgressCancelling(progressId); if (stopBtn) { stopBtn.disabled = true; - stopBtn.textContent = '取消中...'; + stopBtn.textContent = typeof window.t === 'function' ? window.t('tasks.cancelling') : '取消中...'; } try { @@ -686,7 +689,7 @@ function handleStreamEvent(event, progressElement, progressId, case 'error': // 显示错误 addTimelineItem(timeline, 'error', { - title: '❌ 错误', + title: '❌ ' + (typeof window.t === 'function' ? window.t('chat.error') : '错误'), message: event.message, data: event.data }); @@ -694,7 +697,7 @@ function handleStreamEvent(event, progressElement, progressId, // 更新进度标题为错误状态 const errorTitle = document.querySelector(`#${progressId} .progress-title`); if (errorTitle) { - errorTitle.textContent = '❌ 执行失败'; + errorTitle.textContent = '❌ ' + (typeof window.t === 'function' ? window.t('chat.executionFailed') : '执行失败'); } // 更新进度容器为已完成状态(添加completed类) @@ -759,7 +762,7 @@ function handleStreamEvent(event, progressElement, progressId, // 完成,更新进度标题(如果进度消息还存在) const doneTitle = document.querySelector(`#${progressId} .progress-title`); if (doneTitle) { - doneTitle.textContent = '✅ 渗透测试完成'; + doneTitle.textContent = '✅ ' + (typeof window.t === 'function' ? window.t('chat.penetrationTestComplete') : '渗透测试完成'); } // 更新对话ID if (event.data && event.data.conversationId) { @@ -872,7 +875,8 @@ function addTimelineItem(timeline, type, options) { eventTime = new Date(); } - const time = eventTime.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit', second: '2-digit' }); + const timeLocale = (typeof window.__locale === 'string' && window.__locale.startsWith('zh')) ? 'zh-CN' : 'en-US'; + const time = eventTime.toLocaleTimeString(timeLocale, { hour: '2-digit', minute: '2-digit', second: '2-digit' }); let content = `
@@ -945,7 +949,7 @@ async function loadActiveTasks(showErrors = false) { const result = await response.json().catch(() => ({})); if (!response.ok) { - throw new Error(result.error || '获取活跃任务失败'); + throw new Error(result.error || (typeof window.t === 'function' ? window.t('tasks.loadActiveTasksFailed') : '获取活跃任务失败')); } renderActiveTasks(result.tasks || []); @@ -953,7 +957,8 @@ async function loadActiveTasks(showErrors = false) { console.error('获取活跃任务失败:', error); if (showErrors && bar) { bar.style.display = 'block'; - bar.innerHTML = `
无法获取任务状态:${escapeHtml(error.message)}
`; + const cannotGetStatus = typeof window.t === 'function' ? window.t('tasks.cannotGetTaskStatus') : '无法获取任务状态:'; + bar.innerHTML = `
${escapeHtml(cannotGetStatus)}${escapeHtml(error.message)}
`; } } } @@ -982,8 +987,9 @@ function renderActiveTasks(tasks) { item.className = 'active-task-item'; const startedTime = task.startedAt ? new Date(task.startedAt) : null; + const taskTimeLocale = (typeof window.__locale === 'string' && window.__locale.startsWith('zh')) ? 'zh-CN' : 'en-US'; const timeText = startedTime && !isNaN(startedTime.getTime()) - ? startedTime.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit', second: '2-digit' }) + ? startedTime.toLocaleTimeString(taskTimeLocale, { hour: '2-digit', minute: '2-digit', second: '2-digit' }) : ''; const _t = function (k) { return typeof window.t === 'function' ? window.t(k) : k; };