From 53966e03b503c5ed16d829b32bd87aa3506522f5 Mon Sep 17 00:00:00 2001 From: potaser Date: Sun, 9 Aug 2026 12:45:13 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A4=9A=E8=AF=AD=E8=A8=80=E6=95=A3=E8=90=BD?= =?UTF-8?q?=E4=BD=8D=E7=BD=AE=E8=A1=A5=E5=85=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/vue3/index_ml.html | 356 ++++++++++++++++---------------- apps/web/vue3/static/i18n_en.js | 339 +++++++++++++++++++++++++++++- 2 files changed, 516 insertions(+), 179 deletions(-) diff --git a/apps/web/vue3/index_ml.html b/apps/web/vue3/index_ml.html index 936fd3ad9..0f5967946 100644 --- a/apps/web/vue3/index_ml.html +++ b/apps/web/vue3/index_ml.html @@ -8620,7 +8620,7 @@ RNA-seq KEGG/fgsea 会优先使用物种级 KEGG gene set;没有物种级缓 ElMessage.success(autofillState.type === 'config' ? '已写入正式 config.xlsx' : '已写入正式 samples.yaml'); autofillDialogVisible.value = false; if (autofillState.type === 'config' && data.resume_goal) { - await addAgentMessage('assistant', `config.xlsx 已确认,继续当前目标:${data.resume_goal}`); + await addAgentMessage('assistant', t('config.xlsx 已确认,继续当前目标:') + data.resume_goal); } }; const loadFromPath = async (options = {}) => { @@ -12352,14 +12352,14 @@ compare: [[flesh, peel]]`; }; const executePerSpeciesEvidenceStage = async (stageId, baseParams = {}, executor) => { const speciesList = subfamilyEvidenceFanoutSpecies(baseParams); - addAgentMessage('assistant', `按 per_species 拆分 ${getStageLabel(stageId)}:${speciesList.join(', ')}`); + addAgentMessage('assistant', t('按 per_species 拆分 ') + getStageLabel(stageId) + ':' + speciesList.join(', ')); const results = []; for (const species of speciesList) { if (shouldStopBatchExecute.value) { throw new Error('Batch execution stopped'); } const speciesParams = perSpeciesEvidenceParams(stageId, baseParams, species); - addAgentMessage('assistant', `⚙️ ${getStageLabel(stageId)} (${species})...`); + addAgentMessage('assistant', '⚙️ ' + getStageLabel(stageId) + ' (' + species + ')...'); const result = await executor(stageId, speciesParams); results.push({ species, result }); } @@ -13112,7 +13112,7 @@ compare: [[flesh, peel]]`; autoAnnotationConfirmData.value = payload || {}; autoAnnotationCountdown.value = 40; autoAnnotationDialogVisible.value = true; - addAgentMessage('assistant', `⚠️ 检测到 ${autoAnnotationBackgroundTitle(payload)},将在确认后启动本地自动注释。`); + addAgentMessage('assistant', t('⚠️ 检测到 ') + autoAnnotationBackgroundTitle(payload) + t(',将在确认后启动本地自动注释。')); return new Promise((resolve) => { autoAnnotationResolve = resolve; autoAnnotationTimer = setInterval(() => { @@ -13798,13 +13798,13 @@ compare: [[flesh, peel]]`; stageExecutionStatus.value[stageId] = 'pending'; }); if (isBatchExecuting.value) { - addAgentMessage('assistant', `🔄 ${reason || '参考数据已就绪'};Ortholog/PPI/KaKs 已恢复为待执行,将随当前批次继续调度。`); + addAgentMessage('assistant', '🔄 ' + (reason || t('参考数据已就绪')) + t(';Ortholog/PPI/KaKs 已恢复为待执行,将随当前批次继续调度。')); return; } isBatchExecuting.value = true; shouldStopBatchExecute.value = false; try { - addAgentMessage('assistant', `🔄 ${reason || '参考数据已就绪'};开始续跑 Ortholog/PPI/KaKs。`); + addAgentMessage('assistant', '🔄 ' + (reason || t('参考数据已就绪')) + t(';开始续跑 Ortholog/PPI/KaKs。')); await executeStagesWithDependencies(retryStages, new Set(retryStages.map(stage => getStageId(stage)).filter(Boolean))); } finally { isBatchExecuting.value = false; @@ -13816,7 +13816,7 @@ compare: [[flesh, peel]]`; const decision = await requestPpiDownloadFallbackDecision(err, resolution, recommendation, requestedStages); if (decision === 'ath') { applyArabidopsisFallbackForPpiKaks(requestedStages, err?.message || String(err || 'reference_bundle_download_failed')); - addAgentMessage('assistant', '✅ 已确认使用拟南芥作为 PPI/KaKs 的显式兜底参考;该选择只写入 Ortholog/PPI/KaKs 专用参数,其它常规阶段不受影响。'); + addAgentMessage('assistant', t('✅ 已确认使用拟南芥作为 PPI/KaKs 的显式兜底参考;该选择只写入 Ortholog/PPI/KaKs 专用参数,其它常规阶段不受影响。')); if (requestedStages.includes('ppi')) { const requestBody = buildPpiKaksDependencyRequest(['ppi']); await orchestratePpiOwnerAfterReferenceReady({ @@ -14085,7 +14085,7 @@ compare: [[flesh, peel]]`; mergePpiReferenceSpeciesIntoBatch(refCode, ['kaks']); } clearReferenceBundleDownloadProgress(); - addAgentMessage('assistant', `✅ 推荐参考数据下载完成:${recommendation.species_name || result.species_name || refCode} (${refCode || 'reference'})。已写入 Ortholog/PPI/Ka/Ks 专用参考物种。`); + addAgentMessage('assistant', t('✅ 推荐参考数据下载完成:') + (recommendation.species_name || result.species_name || refCode) + ' (' + (refCode || 'reference') + t(')。已写入 Ortholog/PPI/Ka/Ks 专用参考物种。')); if (requestedStages.includes('ppi')) { const requestBody = buildPpiKaksDependencyRequest(['ppi']); await orchestratePpiOwnerAfterReferenceReady({ @@ -14372,11 +14372,11 @@ compare: [[flesh, peel]]`; const stageIds = stages.map(stage => getStageId(stage)).filter(Boolean); const recoveredReferenceApplied = applyResolvedPpiReferenceToBatch(resolution, requestedStages, requestBody); if (recoveredReferenceApplied) { - addAgentMessage('assistant', `✅ PPI/KaKs 前置数据检查通过:检测到已下载近源参考 ${resolution.reference_species},将用该参考先生成 Ortholog mapping。`); + addAgentMessage('assistant', t('✅ PPI/KaKs 前置数据检查通过:检测到已下载近源参考 ') + resolution.reference_species + t(',将用该参考先生成 Ortholog mapping。')); } const mappingLabel = ppiOrthologMappingLabel(resolution); if (mappingLabel && resolution?.status !== 'needs_ortholog') { - addAgentMessage('assistant', `✅ PPI/KaKs 前置数据检查通过:检测到已有 Ortholog 映射(${mappingLabel}),将复用该映射执行 ${requestedStages.join('、')}。`); + addAgentMessage('assistant', t('✅ PPI/KaKs 前置数据检查通过:检测到已有 Ortholog 映射(') + mappingLabel + t('),将复用该映射执行 ') + requestedStages.join('、') + '。'); return true; } attachOrthologDependencyToOwners(requestedStages); @@ -14394,7 +14394,7 @@ compare: [[flesh, peel]]`; : stage )); } - addAgentMessage('assistant', '✅ PPI/KaKs 前置数据检查通过:当前批次已有 Ortholog,将先生成同源映射再继续下游分析。'); + addAgentMessage('assistant', t('✅ PPI/KaKs 前置数据检查通过:当前批次已有 Ortholog,将先生成同源映射再继续下游分析。')); return true; } const insertAt = Math.max(0, stages.findIndex(stage => ppiKaksDependencyStages.has(getStageId(stage)))); @@ -14424,7 +14424,7 @@ compare: [[flesh, peel]]`; const reason = resolution?.issue_code === 'stale_ortholog_reference_conflict' ? '已有 Ortholog 映射与当前 PPI/KaKs 参考物种不一致' : '缺少可用同源映射'; - addAgentMessage('assistant', `✅ PPI/KaKs 前置数据检查通过:${reason},已自动插入 Ortholog,将先生成 mapping 再执行 ${requestedStages.join('、')}。`); + addAgentMessage('assistant', t('✅ PPI/KaKs 前置数据检查通过:') + reason + t(',已自动插入 Ortholog,将先生成 mapping 再执行 ') + requestedStages.join('、') + '。'); return true; }; @@ -14486,12 +14486,12 @@ compare: [[flesh, peel]]`; return { ...base, params: { ...(base.params || {}), ppiSpecies: explicitPpiSpecies } }; }); clearPpiKaksReferencePause(requestedStages); - addAgentMessage('assistant', `✅ 已选择项目参考物种 ${projectReference};将按该参考检查或生成 Ortholog,并执行 ${requestedStages.join('、')}。`); + addAgentMessage('assistant', t('✅ 已选择项目参考物种 ') + projectReference + t(';将按该参考检查或生成 Ortholog,并执行 ') + requestedStages.join('、') + '。'); return true; } if (decision === 'download') { try { - addAgentMessage('assistant', '⬇️ 正在提交推荐近源参考物种注释包后台下载;Ortholog/PPI/KaKs 将等待参考数据,其它阶段继续执行。'); + addAgentMessage('assistant', t('⬇️ 正在提交推荐近源参考物种注释包后台下载;Ortholog/PPI/KaKs 将等待参考数据,其它阶段继续执行。')); const download = await downloadRecommendedPpiReferenceBundle(preflight.resolution, requestedStages); if (download.task?.task_id) { const recommendation = download.recommendation || {}; @@ -14505,7 +14505,7 @@ compare: [[flesh, peel]]`; reference_download_task_id: download.task.task_id }, pauseMessage); monitorPpiReferenceBundleDownload(download.task.task_id, recommendation, requestedStages, preflight.resolution); - addAgentMessage('assistant', `⏳ 推荐参考数据下载已转入后台:${recommendation.species_name || recommendation.species_code || 'reference'},task_id=${download.task.task_id}。`); + addAgentMessage('assistant', t('⏳ 推荐参考数据下载已转入后台:') + (recommendation.species_name || recommendation.species_code || 'reference') + t(',task_id=') + download.task.task_id + '。'); return true; } const recommendation = download.recommendation || {}; @@ -14518,7 +14518,7 @@ compare: [[flesh, peel]]`; if (ownerStageId === 'kaks' && refCode) { mergePpiReferenceSpeciesIntoBatch(refCode, ['kaks']); } - addAgentMessage('assistant', `✅ 推荐参考数据下载完成:${recommendation.species_name || refCode} (${refCode})。已写入 Ortholog/PPI/Ka/Ks 专用参考物种。`); + addAgentMessage('assistant', t('✅ 推荐参考数据下载完成:') + (recommendation.species_name || refCode) + ' (' + refCode + t(')。已写入 Ortholog/PPI/Ka/Ks 专用参考物种。')); if (ownerStageId === 'ppi') { await orchestratePpiOwnerAfterReferenceReady({ requestBody, @@ -14549,7 +14549,7 @@ compare: [[flesh, peel]]`; } const pauseMessage = formatPpiSpeciesResolutionBlockMessage(preflight.resolution); storePpiKaksReferencePause(requestedStages, preflight.resolution, pauseMessage); - addAgentMessage('assistant', `⏸️ 已按选择先暂停 Ortholog/PPI/KaKs,其它阶段继续执行。\n${pauseMessage}`); + addAgentMessage('assistant', t('⏸️ 已按选择先暂停 Ortholog/PPI/KaKs,其它阶段继续执行。') + '\n' + pauseMessage); return true; } if (!preflight.allowed) return false; @@ -14558,7 +14558,7 @@ compare: [[flesh, peel]]`; if (preflight.resolution?.status !== 'needs_ortholog') { const mappingLabel = ppiOrthologMappingLabel(preflight.resolution); if (preflight.resolution?.status === 'ready' && mappingLabel && !stageIds.includes('ortholog')) { - addAgentMessage('assistant', `✅ PPI/KaKs 前置数据检查通过:检测到已有 Ortholog 映射(${mappingLabel}),将直接执行 ${requestedStages.join('、')}。`); + addAgentMessage('assistant', t('✅ PPI/KaKs 前置数据检查通过:检测到已有 Ortholog 映射(') + mappingLabel + t('),将直接执行 ') + requestedStages.join('、') + '。'); } return true; } @@ -14639,10 +14639,10 @@ compare: [[flesh, peel]]`; : (stageId === 'rnaseq_kegg_enrich' ? 'gene2ko' : 'gene2go'); const accepted = await requestAutoAnnotationConfirmation(annotationPayload); if (!accepted) throw createStructuredExecuteError({ ...data, message: `已取消自动注释,${getStageLabel(stageId)} 未提交。` }); - addAgentMessage('assistant', `✅ 已确认自动注释,正在重新提交 ${getStageLabel(stageId)} 到后端;系统会先生成 ${backgroundLabel},再继续富集分析。`); + addAgentMessage('assistant', t('✅ 已确认自动注释,正在重新提交 ') + getStageLabel(stageId) + t(' 到后端;系统会先生成 ') + backgroundLabel + t(',再继续富集分析。')); data = await submit({ ...requestBody, enrichBackgroundSource: 'annotation', confirmedAutoAnnotation: true }); if (data.success && data.task_id) { - addAgentMessage('assistant', `🧬 自动背景注释与 ${getStageLabel(stageId)} 已提交后端,task_id=${data.task_id}`); + addAgentMessage('assistant', t('🧬 自动背景注释与 ') + getStageLabel(stageId) + t(' 已提交后端,task_id=') + data.task_id); } } return data; @@ -14818,7 +14818,7 @@ compare: [[flesh, peel]]`; return true; } catch (err) { console.error(`[${errorPrefix}] failed:`, err); - addAgentMessage('assistant', `⚠️ ${errorPrefix}:${err.message || err}`); + addAgentMessage('assistant', '⚠️ ' + errorPrefix + ':' + (err.message || err)); return false; } }; @@ -14867,7 +14867,7 @@ compare: [[flesh, peel]]`; || previousPlanParams.postConfirmMessage || fallbackMessage; try { - addAgentMessage('assistant', '🔁 亚家族标签已确认,正在重新规划确认后常规基因家族分析...'); + addAgentMessage('assistant', t('🔁 亚家族标签已确认,正在重新规划确认后常规基因家族分析...')); const response = await fetch('/api/agent/plan', { method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -14895,7 +14895,7 @@ compare: [[flesh, peel]]`; } const workflowPhase = String(plan.workflowPhase || plan.params?.workflowPhase || planData.intent?.workflowPhase || ''); if (workflowPhase && workflowPhase !== 'post_subfamily_confirm' && workflowPhase !== 'custom_scope') { - addAgentMessage('assistant', `亚家族确认已完成,但后端当前 workflowPhase 为 ${workflowPhase},未自动打开常规分析。`); + addAgentMessage('assistant', t('亚家族确认已完成,但后端当前 workflowPhase 为 ') + workflowPhase + t(',未自动打开常规分析。')); return false; } const completedSet = new Set(planData.completed_stages || []); @@ -14908,7 +14908,7 @@ compare: [[flesh, peel]]`; return stageId && !completedSet.has(stageId) && !blocked; }); if (!activeExecutionStages.length) { - addAgentMessage('assistant', '确认后常规分析没有新的可执行阶段。'); + addAgentMessage('assistant', t('确认后常规分析没有新的可执行阶段。')); return false; } @@ -14938,12 +14938,12 @@ compare: [[flesh, peel]]`; await loadInterspeciesSpeciesOptions(); } batchExecuteDialogVisible.value = true; - addAgentMessage('assistant', `⚙️ 亚家族确认后已规划 ${activeExecutionStages.length} 个常规分析阶段,请确认参数`); + addAgentMessage('assistant', t('⚙️ 亚家族确认后已规划 ') + activeExecutionStages.length + t(' 个常规分析阶段,请确认参数')); startBatchExecuteTimer(); return true; } catch (err) { console.error('[contextual post-confirm continuation] failed:', err); - addAgentMessage('assistant', `⚠️ 亚家族确认后自动续跑规划失败:${err.message || err}`); + addAgentMessage('assistant', t('⚠️ 亚家族确认后自动续跑规划失败:') + (err.message || err)); return false; } }; @@ -15056,7 +15056,7 @@ compare: [[flesh, peel]]`; normalizeBatchExecutionSpeciesForStages(); const dependencyPreflightOk = await ensureOrthologProducerForPpiKaks(); if (!dependencyPreflightOk) { - addAgentMessage('assistant', '⏸️ PPI/KaKs 前置数据检查未通过,已暂停批量执行;请补齐参考注释包或确认参考物种后重新执行。'); + addAgentMessage('assistant', t('⏸️ PPI/KaKs 前置数据检查未通过,已暂停批量执行;请补齐参考注释包或确认参考物种后重新执行。')); return; } isBatchExecuting.value = true; @@ -15065,7 +15065,7 @@ compare: [[flesh, peel]]`; const stages = batchExecuteStages.value; const plannedStageIds = new Set(stages.map(stage => getStageId(stage)).filter(Boolean)); - addAgentMessage('assistant', `🚀 准备提交依赖驱动执行,共 ${stages.length} 个阶段...`); + addAgentMessage('assistant', t('🚀 准备提交依赖驱动执行,共 ') + stages.length + t(' 个阶段...')); // 初始化阶段状态 stageExecutionStatus.value = {}; @@ -15137,11 +15137,11 @@ compare: [[flesh, peel]]`; const totalCount = stages.length; if (failedCount > 0) { - addAgentMessage('assistant', `⚠️ 批量执行完成:成功 ${completedCount}/${totalCount},失败 ${failedCount}/${totalCount}`); + addAgentMessage('assistant', t('⚠️ 批量执行完成:成功 ') + completedCount + '/' + totalCount + t(',失败 ') + failedCount + '/' + totalCount); } else if (pausedCount > 0) { addAgentMessage('assistant', pausedStageSummaryText(stages, completedCount, totalCount)); } else { - addAgentMessage('assistant', `🎉 批量执行完成!成功: ${completedCount}/${totalCount}`); + addAgentMessage('assistant', t('🎉 批量执行完成!成功: ') + completedCount + '/' + totalCount); if (isMethodologyDataPreparationContinuationCandidate(stages, completedCount, totalCount, failedCount, pausedCount)) { await openMethodologyDataPreparationContinuation(); } else if (isMethodologyPostConfirmContinuationCandidate(stages, completedCount, totalCount, failedCount, pausedCount)) { @@ -15270,7 +15270,7 @@ compare: [[flesh, peel]]`; if (pausedDeps.length > 0) { const pausedNames = pausedDeps.map(s => typeof s === 'object' ? (s.name || getStageLabel(getStageAction(s))) : s).join(', '); - addAgentMessage('assistant', `⏸️ 以下阶段等待上游暂停项处理后继续: ${pausedNames}`); + addAgentMessage('assistant', t('⏸️ 以下阶段等待上游暂停项处理后继续: ') + pausedNames); pausedDeps.forEach(stage => { const stageId = typeof stage === 'object' ? stage.id : stage; stageExecutionStatus.value[stageId] = 'paused'; @@ -15299,7 +15299,7 @@ compare: [[flesh, peel]]`; if (failedDeps.length > 0) { const failedNames = failedDeps.map(s => typeof s === 'object' ? (s.name || getStageLabel(getStageAction(s))) : s).join(', '); - addAgentMessage('assistant', `⚠️ 以下阶段因依赖失败被跳过: ${failedNames}`); + addAgentMessage('assistant', t('⚠️ 以下阶段因依赖失败被跳过: ') + failedNames); failedDeps.forEach(stage => { const stageId = typeof stage === 'object' ? stage.id : stage; stageExecutionStatus.value[stageId] = 'failed'; @@ -15331,7 +15331,7 @@ compare: [[flesh, peel]]`; stageExecutionStatus.value[stageId] = "failed"; } }); - addAgentMessage("assistant", "亚家族标签确认被跳过,已停止依赖 confirmed_subfamily.tsv 的后续阶段;如需普通无亚家族标签分析,请重新发起并明确说明“不需要亚家族”。"); + addAgentMessage("assistant", t('亚家族标签确认被跳过,已停止依赖 confirmed_subfamily.tsv 的后续阶段;如需普通无亚家族标签分析,请重新发起并明确说明"不需要亚家族"。')); }; const confirmedStageParamsForExecution = (stageId, stageParams = {}) => { @@ -15456,7 +15456,7 @@ compare: [[flesh, peel]]`; if (!shouldExecute) { console.log(`[DEBUG-STAGE] ${stageId}: user skipped`); - addAgentMessage('assistant', `⏭️ 跳过阶段: ${stageName}`); + addAgentMessage('assistant', t('⏭️ 跳过阶段: ') + stageName); if (stageAction === "subfamily_confirm") { stopBatchAfterSkippedSubfamilyConfirm(); stageExecutionStatus.value[stageId] = "failed"; @@ -15476,7 +15476,7 @@ compare: [[flesh, peel]]`; return; } - addAgentMessage('assistant', `⚙️ 提交阶段: ${stageName}...`); + addAgentMessage('assistant', t('⚙️ 提交阶段: ') + stageName + '...'); // 执行阶段 console.log(`[DEBUG-STAGE] ${stageId}: calling executeSingleStageWithParams`); @@ -15537,7 +15537,7 @@ compare: [[flesh, peel]]`; reason: 'methodology_member_review_required', message: 'identify 已完成,但方法学链路需要先确认基因家族成员表' }); - addAgentMessage('assistant', '⏸️ identify 已完成,但方法学链路需要先确认基因家族成员表;保存成员确认后再继续 phylogeny。'); + addAgentMessage('assistant', t('⏸️ identify 已完成,但方法学链路需要先确认基因家族成员表;保存成员确认后再继续 phylogeny。')); return; } clearStagePause(stageId); @@ -15559,7 +15559,7 @@ compare: [[flesh, peel]]`; reason: 'confirmed_subfamily_missing', message: err.message || '亚家族标签尚未确认' }); - addAgentMessage('assistant', `⏸️ ${err.message} 已暂停依赖 confirmed_subfamily.tsv 的后续阶段;请在亚家族确认表中保存后重新拉取规划继续执行。`); + addAgentMessage('assistant', '⏸️ ' + err.message + t(' 已暂停依赖 confirmed_subfamily.tsv 的后续阶段;请在亚家族确认表中保存后重新拉取规划继续执行。')); return; } if (await openIdentifyMemberReviewFromBlockedStage( @@ -15583,14 +15583,14 @@ compare: [[flesh, peel]]`; message: pauseMessage, resolution }); - addAgentMessage('assistant', `⏸️ ${stageName} 暂停:需要先准备参考物种 PEP/CDS 或 Ortholog 映射。\n${pauseMessage}`); + addAgentMessage('assistant', t('⏸️ ') + stageName + t(' 暂停:需要先准备参考物种 PEP/CDS 或 Ortholog 映射。') + '\n' + pauseMessage); return; } if (err.status === 'preflight_failed') { shouldStopBatchExecute.value = true; stageExecutionStatus.value[stageId] = 'failed'; addAgentMessage('assistant', await showFailureDiagnosis(stageAction, err, { fallbackMessage: err.message })); - addAgentMessage('assistant', '⏹️ 后端预检失败,当前批次已停止;请重新拉取规划后继续。'); + addAgentMessage('assistant', t('⏹️ 后端预检失败,当前批次已停止;请重新拉取规划后继续。')); console.error('[ERROR] Stage preflight failed:', stageId, err); return; } @@ -15603,13 +15603,13 @@ compare: [[flesh, peel]]`; if (wgsFallbackResult) { stageExecutionStatus.value[stageId] = 'completed'; clearStagePause(stageId); - addAgentMessage('assistant', `✅ 阶段 ${stageName} 已按用户确认使用 bcftools 完成`); + addAgentMessage('assistant', t('✅ 阶段 ') + stageName + t(' 已按用户确认使用 bcftools 完成')); return; } const shouldRetryWithFallback = await maybePromptEnrichBackgroundFallback(err.message, stageAction); if (shouldRetryWithFallback) { try { - addAgentMessage('assistant', `🔄 阶段 ${getStageLabel(stageAction)} 重新提交(自动注释背景)...`); + addAgentMessage('assistant', t('🔄 阶段 ') + getStageLabel(stageAction) + t(' 重新提交(自动注释背景)...')); const retryParams = confirmedStageParamsForExecution(stageAction, typeof stage === 'object' ? (stage.params || {}) : {}); const retryResult = await executeSingleStageWithParams(stageAction, retryParams); addInterpretationButton(stageAction, stageName, { @@ -15627,7 +15627,7 @@ compare: [[flesh, peel]]`; reason: 'methodology_member_review_required', message: 'identify 已完成,但方法学链路需要先确认基因家族成员表' }); - addAgentMessage('assistant', '⏸️ identify 已完成,但方法学链路需要先确认基因家族成员表;保存成员确认后再继续 phylogeny。'); + addAgentMessage('assistant', t('⏸️ identify 已完成,但方法学链路需要先确认基因家族成员表;保存成员确认后再继续 phylogeny。')); return; } clearStagePause(stageId); @@ -16126,7 +16126,7 @@ compare: [[flesh, peel]]`; if (!data.success) { throw createStructuredExecuteError(data); } - addAgentMessage('assistant', '进化树图重绘已提交:task_id=' + (data.task_id || 'sync')); + addAgentMessage('assistant', t('进化树图重绘已提交:task_id=') + (data.task_id || 'sync')); if (data.task_id) { activePipelineTaskIds.value = [...activePipelineTaskIds.value, data.task_id]; try { @@ -16150,7 +16150,7 @@ compare: [[flesh, peel]]`; console.error('[Tree Replot] submit failed:', err); const msg = err && err.message ? err.message : String(err); ElMessage.error('进化树图重绘失败:' + msg); - addAgentMessage('assistant', '进化树图重绘失败:' + msg); + addAgentMessage('assistant', t('进化树图重绘失败:') + msg); return null; } finally { isExecuting.value = false; @@ -16253,7 +16253,7 @@ compare: [[flesh, peel]]`; if (!data.success) { throw createStructuredExecuteError(data); } - addAgentMessage('assistant', `Combo 图修订已提交:task_id=${data.task_id || 'sync'};panels=${panels.join(',')}`); + addAgentMessage('assistant', t('Combo 图修订已提交:task_id=') + (data.task_id || 'sync') + ';panels=' + panels.join(',')); if (data.task_id) { activePipelineTaskIds.value = [...activePipelineTaskIds.value, data.task_id]; try { @@ -16274,7 +16274,7 @@ compare: [[flesh, peel]]`; console.error('[Combo Replot] submit failed:', err); const msg = err && err.message ? err.message : String(err); ElMessage.error('Combo 图修订失败:' + msg); - addAgentMessage('assistant', 'Combo 图修订失败:' + msg); + addAgentMessage('assistant', t('Combo 图修订失败:') + msg); return null; } finally { isExecuting.value = false; @@ -16454,7 +16454,7 @@ compare: [[flesh, peel]]`; batchExecutionUserConfirmed.value = false; batchExecuteDialogVisible.value = false; isBatchExecuting.value = false; - addAgentMessage('assistant', '❌ 批量执行已取消'); + addAgentMessage('assistant', t('❌ 批量执行已取消')); }; const prepareGwasConfirmation = (params = {}) => { @@ -17486,7 +17486,7 @@ compare: [[flesh, peel]]`; if (data.task_id) { // 异步任务,轮询状态。只有拿到 task_id 后才认为后端已接收。 console.log(`[DEBUG-EXEC] ${stageName}: async task, task_id=${data.task_id}`); - addAgentMessage('assistant', `🚀 阶段 ${getStageLabel(stageName)} 已提交后端,task_id=${data.task_id}`); + addAgentMessage('assistant', t('🚀 阶段 ') + getStageLabel(stageName) + t(' 已提交后端,task_id=') + data.task_id); activePipelineTaskIds.value = [...activePipelineTaskIds.value, data.task_id]; let result; try { @@ -17916,7 +17916,7 @@ compare: [[flesh, peel]]`; if (!recoveryNoticeShown) { recoveryNoticeShown = true; console.warn(`[DEBUG-POLL] taskId=${taskId} polling interrupted, switching to manifest recovery:`, err); - addAgentMessage('assistant', `⚠️ ${getStageLabel(stageName)} 前端连接短暂中断,正在按后端产物状态恢复,不会立即判定阶段失败`); + addAgentMessage('assistant', '⚠️ ' + getStageLabel(stageName) + t(' 前端连接短暂中断,正在按后端产物状态恢复,不会立即判定阶段失败')); } setTimeout(checkStatus, 5000); }; @@ -18391,7 +18391,7 @@ compare: [[flesh, peel]]`; const confirmed = await maybeShowSubfamilyReview('reference_classification', true, prefix, true); openedAny = openedAny || confirmed; if (!confirmed) { - addAgentMessage('assistant', `${prefix} 亚家族分类已完成预测,但确认表尚未保存;后续会继续显示为待确认。`); + addAgentMessage('assistant', prefix + t(' 亚家族分类已完成预测,但确认表尚未保存;后续会继续显示为待确认。')); } } return openedAny; @@ -18432,7 +18432,7 @@ compare: [[flesh, peel]]`; reason: 'curated_members_missing', message: '亚家族证据阶段需要先保存 identify 成员初筛确认表' }); - addAgentMessage('assistant', '⏸️ 已打开 identify 成员初筛确认表;保存后会生成 curated_members,再继续亚家族证据阶段。'); + addAgentMessage('assistant', t('⏸️ 已打开 identify 成员初筛确认表;保存后会生成 curated_members,再继续亚家族证据阶段。')); return true; }; @@ -19316,9 +19316,9 @@ compare: [[flesh, peel]]`; refreshInfo(); const opened = await queueAutofillFromPayload(response, 'upload'); if (opened === 'samples') { - addAgentMessage('assistant', '已根据上传文件生成 samples.yaml 草稿。请在弹窗中核对 run、sample_id、condition、replicate、链特异性和分组角色,确认后再写入正式 samples.yaml。'); + addAgentMessage('assistant', t('已根据上传文件生成 samples.yaml 草稿。请在弹窗中核对 run、sample_id、condition、replicate、链特异性和分组角色,确认后再写入正式 samples.yaml。')); } else if (opened === 'config') { - addAgentMessage('assistant', '已根据上传文件生成 config.xlsx 草稿。请在弹窗中核对物种代码、文件类型、物种角色和 Gene_Family,确认后再写入正式 config.xlsx。'); + addAgentMessage('assistant', t('已根据上传文件生成 config.xlsx 草稿。请在弹窗中核对物种代码、文件类型、物种角色和 Gene_Family,确认后再写入正式 config.xlsx。')); } }; const handleUploadError = (err) => { @@ -19453,7 +19453,7 @@ compare: [[flesh, peel]]`; }; const extensionProgressMessage = async (message, type = 'info') => { pushExtensionStatus(message, type, false); - await addAgentMessage('assistant', `拓展分析阶段:${message}`); + await addAgentMessage('assistant', t('拓展分析阶段:') + message); }; const summarizeExtensionRunError = (run, fallback) => { const error = String(run?.error || run?.stderr || fallback || '执行失败').trim(); @@ -19597,12 +19597,12 @@ compare: [[flesh, peel]]`; const lightweightErrorText = (view, fallback) => { const error = view && view.error || {}; const phase = error.phase || view?.action || 'operation'; - const message = error.message || fallback || '操作失败'; - return `阶段:${phase}\n原因:${message}`; + const message = error.message || fallback || t('操作失败'); + return t('阶段:') + phase + '\n' + t('原因:') + message; }; const lightweightHelp = async (extensionId) => { const response = await extensionApiFetch(`/api/lightweight-extension-stages/${encodeURIComponent(extensionId)}/help`); - if (!response.ok) throw new Error(await extensionApiError(response, '读取拓展分析说明失败')); + if (!response.ok) throw new Error(await extensionApiError(response, t('读取拓展分析说明失败'))); return await response.json(); }; const isLightweightExtensionId = (extensionId) => (lightweightExtensions.value || []).some(item => item.extension_id === extensionId); @@ -19612,8 +19612,8 @@ compare: [[flesh, peel]]`; review: 'publish', publish: 'list', run: 'runtime_result' })[action] || ''; const lightweightCandidateConfirmationSummary = (card) => card.revision_summary - ? `✓ 已确认候选|已修订:${card.revision_summary}` - : '✓ 已确认候选|未作修订'; + ? t('✓ 已确认候选|已修订:') + card.revision_summary + : t('✓ 已确认候选|未作修订'); const lightweightParameterText = (item) => { const label = item.flag || item.fact_id || ''; const value = item.value ?? item.default_value ?? item.default ?? ''; @@ -19623,10 +19623,10 @@ compare: [[flesh, peel]]`; const projection = run.projection || {}; const stdout = projection.stdout || {}; return [ - { item: '实际命令', value: run.command || run.execution_command || '见执行日志' }, - { item: '输出文件', value: (projection.files || []).map(item => item.path).join(';') || '(无文件输出)' }, - { item: '标准输出', value: stdout.present ? (stdout.text || '') : '(无)' }, - { item: '日志', value: Object.values(run.logs || {}).join(';') || '(无)' } + { item: t('实际命令'), value: run.command || run.execution_command || t('见执行日志') }, + { item: t('输出文件'), value: (projection.files || []).map(item => item.path).join(';') || t('(无文件输出)') }, + { item: t('标准输出'), value: stdout.present ? (stdout.text || '') : t('(无)') }, + { item: t('日志'), value: Object.values(run.logs || {}).join(';') || t('(无)') } ]; }; const lightweightRows = (help) => { @@ -19634,11 +19634,11 @@ compare: [[flesh, peel]]`; const parameters = (help.parameters || []).map(lightweightParameterText).filter(Boolean); const outputFiles = ((help.outputs || {}).files || []).map(item => item.path).filter(Boolean); return [ - { item: '功能', value: help.function_summary || '-' }, - { item: '原始命令', value: help.original_command || '-' }, - { item: '输入文件', value: inputs.join(';') || '(无)' }, - { item: '可改参数', value: parameters.join(';') || '(无)' }, - { item: '输出', value: outputFiles.join(';') || (((help.outputs || {}).stdout || {}).present ? '标准输出(直接展示)' : '(无)') } + { item: t('功能'), value: help.function_summary || '-' }, + { item: t('原始命令'), value: help.original_command || '-' }, + { item: t('输入文件'), value: inputs.join(';') || t('(无)') }, + { item: t('可改参数'), value: parameters.join(';') || t('(无)') }, + { item: t('输出'), value: outputFiles.join(';') || (((help.outputs || {}).stdout || {}).present ? t('标准输出(直接展示)') : t('(无)')) } ]; }; const setLightweightRunning = (extensionId, operationToken) => { @@ -19686,7 +19686,7 @@ compare: [[flesh, peel]]`; const poll = async () => { try { const response = await extensionApiFetch(`/api/lightweight-extension-stages/operations/${encodeURIComponent(operationToken)}`); - if (!response.ok) throw new Error(await extensionApiError(response, '读取拓展分析状态失败')); + if (!response.ok) throw new Error(await extensionApiError(response, t('读取拓展分析状态失败'))); const view = await response.json(); const terminal = ['completed', 'failed'].includes(String(view.state || '')); if (!terminal) { @@ -19700,14 +19700,14 @@ compare: [[flesh, peel]]`; card.loading = false; if (view.outcome === 'failed' || view.error || view.state === 'failed') { card.status = 'failed'; - card.status_label = '失败'; + card.status_label = t('失败'); card.type = 'danger'; card.error = lightweightErrorText(view); card.logs = view.logs || (view.error && view.error.logs) || []; if (String(view?.error?.phase || '') === 'semantic_candidate') { - card.title = '拓展分析说明生成失败'; - card.purpose = '原始命令、测试数据和执行事实已经保留;这里只重新生成说明,不会重新运行原始命令。'; - card.summary = '可以直接重试,或取消后检查错误原因。'; + card.title = t('拓展分析说明生成失败'); + card.purpose = t('原始命令、测试数据和执行事实已经保留;这里只重新生成说明,不会重新运行原始命令。'); + card.summary = t('可以直接重试,或取消后检查错误原因。'); card.actions = { retry_semantic: true, cancel: true }; } resolve(view); @@ -19721,9 +19721,9 @@ compare: [[flesh, peel]]`; forgetLightweightOperation(operationToken); card.loading = false; card.status = 'failed'; - card.status_label = '失败'; + card.status_label = t('失败'); card.type = 'danger'; - card.error = `阶段:前端轮询\n原因:${err.message || '无法读取操作状态'}`; + card.error = t('阶段:') + 'Frontend Polling\n' + t('原因:') + (err.message || t('无法读取操作状态')); resolve({ state: 'failed', error: { phase: 'frontend_poll', message: err.message } }); } }; @@ -19737,7 +19737,7 @@ compare: [[flesh, peel]]`; const operationToken = lightweightOperationToken(action); card.loading = true; card.status = 'running'; - card.status_label = '运行中'; + card.status_label = t('运行中'); card.type = 'primary'; try { const response = await extensionApiFetch(endpoint, { @@ -19745,7 +19745,7 @@ compare: [[flesh, peel]]`; headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ operation_token: operationToken, ...payload }) }); - if (!response.ok) throw new Error(await extensionApiError(response, `${action} 提交失败`)); + if (!response.ok) throw new Error(await extensionApiError(response, action + t(' 提交失败'))); rememberLightweightOperation(extensionId, action, operationToken, card.card_id); setLightweightRunning(extensionId, operationToken); return await pollLightweightOperation(extensionId, operationToken, card, onComplete); @@ -19753,20 +19753,20 @@ compare: [[flesh, peel]]`; clearLightweightRunning(operationToken); card.loading = false; card.status = 'failed'; - card.status_label = '失败'; + card.status_label = t('失败'); card.type = 'danger'; - card.error = `阶段:${action}\n原因:${err.message || '提交失败'}`; + card.error = t('阶段:') + action + '\n' + t('原因:') + (err.message || t('提交失败')); return null; } }; - const showLightweightCandidate = async (extensionId, summary = '执行入口已在隔离环境运行。请核对系统记录的事实与说明;确认后才会验证。', revisionSummary = '') => { + const showLightweightCandidate = async (extensionId, summary = t('执行入口已在隔离环境运行。请核对系统记录的事实与说明;确认后才会验证。'), revisionSummary = '') => { const help = await lightweightHelp(extensionId); addLightweightCard({ card_id: `${extensionId}:candidate:${Date.now()}`, status: 'pending', - status_label: '待确认', - title: '拓展分析候选核对', - purpose: '这张卡用于核对系统从真实执行中锁定的命令、输入、参数和输出。', + status_label: t('待确认'), + title: t('拓展分析候选核对'), + purpose: t('这张卡用于核对系统从真实执行中锁定的命令、输入、参数和输出。'), summary, rows: lightweightRows(help), extension_id: extensionId, @@ -19779,16 +19779,16 @@ compare: [[flesh, peel]]`; const hasCode = extensionBuild.codeFiles.length > 0; const hasDataPaths = Boolean(String(extensionBuild.dataPaths || '').trim()); if (!extensionBuild.displayName || (!extensionBuild.dataFiles.length && !hasDataPaths) || (!hasCommand && !hasCode)) { - ElMessage.warning('请填写名称并提供测试数据文件或服务器路径;原始命令和原始代码至少提供一项。'); + ElMessage.warning(t('请填写名称并提供测试数据文件或服务器路径;原始命令和原始代码至少提供一项。')); return; } const extensionId = String(extensionBuild.displayName).trim().toLowerCase() .replace(/[^a-z0-9_-]+/g, '_').replace(/^_+|_+$/g, '') || `extension_${Date.now()}`; const token = lightweightOperationToken('upload'); const card = addLightweightCard({ - card_id: `${extensionId}:upload`, status: 'running', status_label: '运行中', type: 'primary', - title: '正在生成拓展分析', purpose: '系统正在确定执行入口,并运行上传的代码和测试数据,提取不可修改的执行事实。', - summary: '原始包可复现检查中…', extension_id: extensionId + card_id: `${extensionId}:upload`, status: 'running', status_label: t('运行中'), type: 'primary', + title: t('正在生成拓展分析'), purpose: t('系统正在确定执行入口,并运行上传的代码和测试数据,提取不可修改的执行事实。'), + summary: t('原始包可复现检查中…'), extension_id: extensionId }); if (!card) return; extensionBuild.loading = true; @@ -19801,12 +19801,12 @@ compare: [[flesh, peel]]`; extensionBuild.dataFiles.forEach(file => form.append('raw_data_files', file, extensionUploadPath(file))); if (hasDataPaths) form.append('raw_data_paths', extensionBuild.dataPaths); const response = await extensionApiFetch('/api/lightweight-extension-stages/upload', { method: 'POST', body: form }); - if (!response.ok) throw new Error(await extensionApiError(response, '原始包可复现检查提交失败')); - ElMessage.success('文件上传成功,已开始生成并验收'); + if (!response.ok) throw new Error(await extensionApiError(response, t('原始包可复现检查提交失败'))); + ElMessage.success(t('文件上传成功,已开始生成并验收')); rememberLightweightOperation(extensionId, 'upload', token, card.card_id); setLightweightRunning(extensionId, token); await pollLightweightOperation(extensionId, token, card, async () => { - finishLightweightCard(card, '✓ 执行入口已成功复现;候选信息已生成。'); + finishLightweightCard(card, t('✓ 执行入口已成功复现;候选信息已生成。')); extensionBuild.extensionId = extensionId; await loadLightweightExtensionStages(); await showLightweightCandidate(extensionId); @@ -19814,9 +19814,9 @@ compare: [[flesh, peel]]`; } catch (err) { clearLightweightRunning(token); card.status = 'failed'; - card.status_label = '失败'; + card.status_label = t('失败'); card.type = 'danger'; - card.error = `阶段:upload\n原因:${err.message || '原始包可复现检查提交失败'}`; + card.error = t('阶段:upload') + '\n' + t('原因:') + (err.message || t('原始包可复现检查提交失败')); } finally { extensionBuild.loading = false; } @@ -19825,9 +19825,9 @@ compare: [[flesh, peel]]`; await submitLightweightOperation(card.extension_id, 'confirm', `/api/lightweight-extension-stages/${encodeURIComponent(card.extension_id)}/confirm`, {}, card, async () => { finishLightweightCard(card, lightweightCandidateConfirmationSummary(card)); addLightweightCard({ - card_id: `${card.extension_id}:validate`, status: 'pending', status_label: '待执行', - title: '开始验证', purpose: '这张卡用于用同一份测试数据执行已确认的工具,并生成可核对的结果。', - summary: '确认无误后点击“开始验证”。', extension_id: card.extension_id, + card_id: `${card.extension_id}:validate`, status: 'pending', status_label: t('待执行'), + title: t('开始验证'), purpose: t('这张卡用于用同一份测试数据执行已确认的工具,并生成可核对的结果。'), + summary: t('确认无误后点击“开始验证”。'), extension_id: card.extension_id, actions: { validate: true } }); }); @@ -19837,25 +19837,25 @@ compare: [[flesh, peel]]`; lightweightExtensionConversation.pendingRevisionCardId = ''; } card.status = 'cancelled'; - card.status_label = '已取消'; - finishLightweightCard(card, '已取消;没有提交后续执行。'); + card.status_label = t('已取消'); + finishLightweightCard(card, t('已取消;没有提交后续执行。')); }; const beginLightweightRevision = (card) => { lightweightExtensionConversation.pendingRevisionCardId = card.card_id; - card.summary = '请在页面下方的主对话输入框说明需要修订的内容,然后发送。命令和真实执行事实不会被修改。'; + card.summary = t('请在页面下方的主对话输入框说明需要修订的内容,然后发送。命令和真实执行事实不会被修改。'); }; const reviseLightweightCandidate = async (card, feedback) => { const normalizedFeedback = String(feedback || '').trim(); if (!normalizedFeedback) { - ElMessage.warning('请写明修订意见。'); + ElMessage.warning(t('请写明修订意见。')); return; } return await submitLightweightOperation(card.extension_id, 'revise', `/api/lightweight-extension-stages/${encodeURIComponent(card.extension_id)}/revise`, { feedback: normalizedFeedback, revision_target: 'semantic' }, card, async () => { - const summary = `已修订:${normalizedFeedback}`; - finishLightweightCard(card, `✓ ${summary}`); + const summary = t('已修订:') + normalizedFeedback; + finishLightweightCard(card, '✓ ' + summary); await showLightweightCandidate(card.extension_id, summary, normalizedFeedback); }); }; @@ -19867,23 +19867,23 @@ compare: [[flesh, peel]]`; {}, card, async () => { - finishLightweightCard(card, '✓ 语义说明已重新生成;执行命令未重新运行。'); + finishLightweightCard(card, t('✓ 语义说明已重新生成;执行命令未重新运行。')); await showLightweightCandidate(card.extension_id); } ); }; const validationRows = async (extensionId, runId) => { const response = await extensionApiFetch(`/api/lightweight-extension-stages/${encodeURIComponent(extensionId)}/results`); - if (!response.ok) throw new Error(await extensionApiError(response, '读取验证结果失败')); + if (!response.ok) throw new Error(await extensionApiError(response, t('读取验证结果失败'))); const data = await response.json(); return lightweightResultRows(lightweightRunById(data.validation_runs, runId)); }; const validateLightweightExtension = async (card) => { await submitLightweightOperation(card.extension_id, 'validate', `/api/lightweight-extension-stages/${encodeURIComponent(card.extension_id)}/validate`, {}, card, async (view) => { - finishLightweightCard(card, '✓ 验证已完成;结果核对卡已生成。'); + finishLightweightCard(card, t('✓ 验证已完成;结果核对卡已生成。')); addLightweightCard({ - card_id: `${card.extension_id}:result_review:${view.run_id}`, status: 'pending', status_label: '待核对', - title: '运行结果核对', purpose: '这张卡用于查看实际命令、输出与日志;核对无误后才允许授权发布。', + card_id: `${card.extension_id}:result_review:${view.run_id}`, status: 'pending', status_label: t('待核对'), + title: t('运行结果核对'), purpose: t('这张卡用于查看实际命令、输出与日志;核对无误后才允许授权发布。'), rows: await validationRows(card.extension_id, view.run_id), extension_id: card.extension_id, actions: { review: true } }); @@ -19891,18 +19891,18 @@ compare: [[flesh, peel]]`; }; const reviewLightweightResult = async (card) => { await submitLightweightOperation(card.extension_id, 'review', `/api/lightweight-extension-stages/${encodeURIComponent(card.extension_id)}/review`, {}, card, async () => { - finishLightweightCard(card, '✓ 已核对运行结果。'); + finishLightweightCard(card, t('✓ 已核对运行结果。')); addLightweightCard({ - card_id: `${card.extension_id}:publish`, status: 'pending', status_label: '待授权', - title: '授权发布', purpose: '这张卡用于确认将已经验证的工具发布为可复用的拓展分析。', - summary: '确认后才会发布;不会自动发布。', extension_id: card.extension_id, + card_id: `${card.extension_id}:publish`, status: 'pending', status_label: t('待授权'), + title: t('授权发布'), purpose: t('这张卡用于确认将已经验证的工具发布为可复用的拓展分析。'), + summary: t('确认后才会发布;不会自动发布。'), extension_id: card.extension_id, actions: { publish: true, cancel: true } }); }); }; const publishLightweightExtension = async (card) => { await submitLightweightOperation(card.extension_id, 'publish', `/api/lightweight-extension-stages/${encodeURIComponent(card.extension_id)}/publish`, {}, card, async () => { - finishLightweightCard(card, '✓ 已显式授权并发布。'); + finishLightweightCard(card, t('✓ 已显式授权并发布。')); await loadLightweightExtensionStages(); }); }; @@ -19912,15 +19912,15 @@ compare: [[flesh, peel]]`; try { const help = await lightweightHelp(extensionId); addLightweightCard({ - card_id: `${extensionId}:runtime:${Date.now()}`, status: 'pending', status_label: '待确认', kind: 'runtime', - title: '输入文件和参数', purpose: '这张卡用于确认本次新数据运行的输入文件和原始命令已有参数;确认后立即执行,不会再次弹出确认。', + card_id: `${extensionId}:runtime:${Date.now()}`, status: 'pending', status_label: t('待确认'), kind: 'runtime', + title: t('输入文件和参数'), purpose: t('这张卡用于确认本次新数据运行的输入文件和原始命令已有参数;确认后立即执行,不会再次弹出确认。'), extension_id: extensionId, inputs: (help.inputs || []).filter(item => item && item.replaceable).map(item => ({ key: item.path, value: '' })), parameters: (help.parameters || []).map(item => ({ key: item.fact_id, label: item.flag || item.fact_id, value: item.value || item.flag || '' })), explicit_prefix: '', actions: { run: true, cancel: true } }); } catch (err) { - ElMessage.error(err.message || '读取运行说明失败'); + ElMessage.error(err.message || t('读取运行说明失败')); } }; const runtimeInputPath = (jobId, value) => { @@ -19932,12 +19932,12 @@ compare: [[flesh, peel]]`; const runLightweightExtension = async (card) => { const jobId = String(jobInfo.job_id || '').trim(); if (!jobId || jobId === 'default_job') { - ElMessage.warning('请先选择实际运行 Job。'); + ElMessage.warning(t('请先选择实际运行 Job。')); return; } const inputOverrides = Object.fromEntries((card.inputs || []).map(item => [item.key, runtimeInputPath(jobId, item.value)])); if (Object.values(inputOverrides).some(value => !value)) { - ElMessage.warning('请填写每个输入文件。'); + ElMessage.warning(t('请填写每个输入文件。')); return; } const parameterOverrides = Object.fromEntries((card.parameters || []).map(item => [item.key, item.value])); @@ -19947,13 +19947,13 @@ compare: [[flesh, peel]]`; parameter_overrides: parameterOverrides, explicit_prefix: String(card.explicit_prefix || '').trim() || null }, card, async (view) => { - finishLightweightCard(card, '✓ 已确认输入与参数;本次运行已完成。'); + finishLightweightCard(card, t('✓ 已确认输入与参数;本次运行已完成。')); const response = await extensionApiFetch(`/api/lightweight-extension-stages/${encodeURIComponent(card.extension_id)}/results`); const data = response.ok ? await response.json() : {}; const run = lightweightRunById(data.runtime_runs, view.run_id); addLightweightCard({ - card_id: `${card.extension_id}:runtime_result:${view.run_id}`, status: 'completed', status_label: '已完成', type: 'success', - title: '运行结果', purpose: '这张卡展示本次新数据运行生成的文件、标准输出和日志。', + card_id: `${card.extension_id}:runtime_result:${view.run_id}`, status: 'completed', status_label: t('已完成'), type: 'success', + title: t('运行结果'), purpose: t('这张卡展示本次新数据运行生成的文件、标准输出和日志。'), extension_id: card.extension_id, rows: lightweightResultRows(run) }); @@ -19969,61 +19969,61 @@ compare: [[flesh, peel]]`; if (!saved || !saved.extensionId || !saved.operationToken || !saved.action) return; const card = addLightweightCard({ card_id: saved.cardId || `${saved.extensionId}:${saved.action}`, - status: 'running', status_label: '运行中', type: 'primary', extension_id: saved.extensionId, - title: '正在恢复拓展分析状态', purpose: '页面已恢复;系统继续轮询同一个操作,不会重复提交。', - summary: `正在恢复 ${saved.action}…` + status: 'running', status_label: t('运行中'), type: 'primary', extension_id: saved.extensionId, + title: t('正在恢复拓展分析状态'), purpose: t('页面已恢复;系统继续轮询同一个操作,不会重复提交。'), + summary: t('正在恢复 ') + saved.action + '…' }); if (!card) return; await pollLightweightOperation(saved.extensionId, saved.operationToken, card, async (view) => { const next = lightweightResumeNext(saved.action); switch (next) { case 'candidate': - finishLightweightCard(card, '✓ 已恢复并完成;候选核对卡已生成。'); + finishLightweightCard(card, t('✓ 已恢复并完成;候选核对卡已生成。')); extensionBuild.extensionId = saved.extensionId; await loadLightweightExtensionStages(); - await showLightweightCandidate(saved.extensionId, saved.action === 'revise' ? '已恢复修订;请核对最新候选。' : undefined); + await showLightweightCandidate(saved.extensionId, saved.action === 'revise' ? t('已恢复修订;请核对最新候选。') : undefined); break; case 'validate': - finishLightweightCard(card, '✓ 已恢复确认;可以开始验证。'); + finishLightweightCard(card, t('✓ 已恢复确认;可以开始验证。')); addLightweightCard({ - card_id: `${saved.extensionId}:validate`, status: 'pending', status_label: '待执行', - title: '开始验证', purpose: '这张卡用于用同一份测试数据执行已确认的工具,并生成可核对的结果。', - summary: '确认无误后点击“开始验证”。', extension_id: saved.extensionId, actions: { validate: true } + card_id: `${saved.extensionId}:validate`, status: 'pending', status_label: t('待执行'), + title: t('开始验证'), purpose: t('这张卡用于用同一份测试数据执行已确认的工具,并生成可核对的结果。'), + summary: t('确认无误后点击“开始验证”。'), extension_id: saved.extensionId, actions: { validate: true } }); break; case 'review': - finishLightweightCard(card, '✓ 已恢复验证;结果核对卡已生成。'); + finishLightweightCard(card, t('✓ 已恢复验证;结果核对卡已生成。')); addLightweightCard({ - card_id: `${saved.extensionId}:result_review:${view.run_id}`, status: 'pending', status_label: '待核对', - title: '运行结果核对', purpose: '这张卡用于查看实际命令、输出与日志;核对无误后才允许授权发布。', + card_id: `${saved.extensionId}:result_review:${view.run_id}`, status: 'pending', status_label: t('待核对'), + title: t('运行结果核对'), purpose: t('这张卡用于查看实际命令、输出与日志;核对无误后才允许授权发布。'), rows: await validationRows(saved.extensionId, view.run_id), extension_id: saved.extensionId, actions: { review: true } }); break; case 'publish': - finishLightweightCard(card, '✓ 已恢复结果核对;可以授权发布。'); + finishLightweightCard(card, t('✓ 已恢复结果核对;可以授权发布。')); addLightweightCard({ - card_id: `${saved.extensionId}:publish`, status: 'pending', status_label: '待授权', - title: '授权发布', purpose: '这张卡用于确认将已经验证的工具发布为可复用的拓展分析。', - summary: '确认后才会发布;不会自动发布。', extension_id: saved.extensionId, actions: { publish: true, cancel: true } + card_id: `${saved.extensionId}:publish`, status: 'pending', status_label: t('待授权'), + title: t('授权发布'), purpose: t('这张卡用于确认将已经验证的工具发布为可复用的拓展分析。'), + summary: t('确认后才会发布;不会自动发布。'), extension_id: saved.extensionId, actions: { publish: true, cancel: true } }); break; case 'list': - finishLightweightCard(card, '✓ 已恢复并发布。'); + finishLightweightCard(card, t('✓ 已恢复并发布。')); await loadLightweightExtensionStages(); break; case 'runtime_result': { - finishLightweightCard(card, '✓ 已恢复运行;本次结果卡已生成。'); + finishLightweightCard(card, t('✓ 已恢复运行;本次结果卡已生成。')); const response = await extensionApiFetch(`/api/lightweight-extension-stages/${encodeURIComponent(saved.extensionId)}/results`); const data = response.ok ? await response.json() : {}; addLightweightCard({ - card_id: `${saved.extensionId}:runtime_result:${view.run_id}`, status: 'completed', status_label: '已完成', type: 'success', - title: '运行结果', purpose: '这张卡展示本次新数据运行生成的文件、标准输出和日志。', + card_id: `${saved.extensionId}:runtime_result:${view.run_id}`, status: 'completed', status_label: t('已完成'), type: 'success', + title: t('运行结果'), purpose: t('这张卡展示本次新数据运行生成的文件、标准输出和日志。'), extension_id: saved.extensionId, rows: lightweightResultRows(lightweightRunById(data.runtime_runs, view.run_id)) }); break; } default: - finishLightweightCard(card, '✓ 已恢复并完成。'); + finishLightweightCard(card, t('✓ 已恢复并完成。')); } }); }; @@ -20052,11 +20052,11 @@ compare: [[flesh, peel]]`; method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message: query }) }); const data = response.ok ? await response.json() : {}; - await addAgentMessage('assistant', data.usage || '上传可执行代码、测试数据和原始命令,核对候选后验证并授权发布;发布后可指定输入和原命令已有参数运行。'); + await addAgentMessage('assistant', data.usage || t('上传可执行代码、测试数据和原始命令,核对候选后验证并授权发布;发布后可指定输入和原命令已有参数运行。')); return true; } const help = await lightweightHelp(matched.extension_id); - await addAgentMessage('assistant', lightweightRows(help).map(row => `${row.item}:${row.value}`).join('\n')); + await addAgentMessage('assistant', lightweightRows(help).map(row => row.item + t(':') + row.value).join('\n')); return true; }; const buildExtensionStageLegacy = async () => { @@ -20137,7 +20137,7 @@ compare: [[flesh, peel]]`; } catch (err) { const message = err.message || '拓展分析阶段生成失败'; pushExtensionStatus(message, 'error', false); - await addAgentMessage('assistant', `拓展分析阶段失败:${message}`); + await addAgentMessage('assistant', t('拓展分析阶段失败:') + message); ElMessage.error(message.split('\n')[0]); } finally { extensionBuild.loading = false; @@ -20396,7 +20396,7 @@ compare: [[flesh, peel]]`; throw new Error(diagnosis || run.error || '拓展分析阶段执行未成功,请查看 stderr.log'); } const outputNames = (run.outputs || []).map(out => out.name).join('、') || '未扫描到输出文件'; - addAgentMessage('assistant', `已执行拓展分析阶段:${params.extensionName || extensionId}。结果已写入当前 job workspace,输出:${outputNames}。`, { + addAgentMessage('assistant', t('已执行拓展分析阶段:') + (params.extensionName || extensionId) + t('。结果已写入当前 job workspace,输出:') + outputNames + '。', { stage: 'extension_stage', taskType: 'extension_stage', params, @@ -20407,7 +20407,7 @@ compare: [[flesh, peel]]`; } catch (err) { extensionRun.statusText = err.message || '执行失败'; if (fromIntent) { - addAgentMessage('assistant', `拓展分析阶段执行失败:${extensionRun.statusText}`, { + addAgentMessage('assistant', t('拓展分析阶段执行失败:') + extensionRun.statusText, { stage: 'extension_stage', taskType: 'extension_stage', params, @@ -21388,7 +21388,7 @@ compare: [[flesh, peel]]`; methodologyStatusText.value = `方法学方案已保存到当前 job:${bindData.plan_path || 'methodology_plan.json'}`; if (!silent) { ElMessage.success('方法学方案已保存,可先准备数据,稍后再按该方案执行'); - await addAgentMessage('assistant', `已保存方法学方案到当前 job。下一步请上传/填写目标物种和参考物种数据,并确认 config.xlsx 后再执行方法学流程。`); + await addAgentMessage('assistant', t('已保存方法学方案到当前 job。下一步请上传/填写目标物种和参考物种数据,并确认 config.xlsx 后再执行方法学流程。')); } return bindData; } catch (err) { @@ -21433,7 +21433,7 @@ compare: [[flesh, peel]]`; methodologyStatusText.value = message; ElMessage.warning(message); await openMethodologyDataPreparation(); - await addAgentMessage('assistant', `已保存方法学方案,但暂不执行:${message}。请在 config.xlsx 自动识别弹窗中核对/补齐数据,确认写入正式配置后再点击“确认并执行”。`, { taskType: 'methodology_data_preparation' }); + await addAgentMessage('assistant', t('已保存方法学方案,但暂不执行:') + message + t('。请在 config.xlsx 自动识别弹窗中核对/补齐数据,确认写入正式配置后再点击”确认并执行”。'), { taskType: 'methodology_data_preparation' }); methodologyExecuting.value = false; return; } @@ -21623,7 +21623,7 @@ compare: [[flesh, peel]]`; } catch (err) { methodologyExecuting.value = false; ElMessage.error(err.message || '方法学执行规划失败'); - addAgentMessage('assistant', '❌ 方法学执行规划失败:' + (err.message || '未知错误'), { + addAgentMessage('assistant', t('❌ 方法学执行规划失败:') + (err.message || 'Unknown error'), { taskType: 'error' }); } @@ -21645,14 +21645,14 @@ compare: [[flesh, peel]]`; taskType: 'analysis' }; - addAgentMessage('assistant', `已选择:${option.label}`); + addAgentMessage('assistant', t('已选择:') + option.label); try { await runPlanForIntent(syntenyChoiceQuery.value || option.label, selectedIntent); } catch (err) { console.error('[DEBUG] Synteny choice plan error:', err); ElMessage.error(`❌ ${err.message || '规划请求失败'}`); - addAgentMessage('assistant', `❌ 服务暂时不可用:${err.message || '规划请求失败'}\n\n请稍后重试。`, { + addAgentMessage('assistant', t('❌ 服务暂时不可用:') + (err.message || 'Planning request failed') + '\n\n' + t('请稍后重试。'), { taskType: 'error' }); } finally { @@ -21924,7 +21924,7 @@ compare: [[flesh, peel]]`; const localStatus = {}; const prefixValue = normalizedPrefix(plan.prefix || params.prefix); - addAgentMessage('assistant', `📌 当前已有前台批次执行中,新任务已转为后台执行:${stageIds.map(getStageLabel).join(', ')}`, { + addAgentMessage('assistant', t('📌 当前已有前台批次执行中,新任务已转为后台执行:') + stageIds.map(getStageLabel).join(', '), { taskType: 'background_batch', batchId, stages: stageIds @@ -21974,11 +21974,11 @@ compare: [[flesh, peel]]`; localStatus[stageId] = 'failed'; failed++; }); - addAgentMessage('assistant', `⚠️ 后台批次中以下阶段因依赖失败被跳过: ${blocked.map(getStageLabel).join(', ')}`); + addAgentMessage('assistant', t('⚠️ 后台批次中以下阶段因依赖失败被跳过: ') + blocked.map(getStageLabel).join(', ')); continue; } - addAgentMessage('assistant', `⚠️ 后台批次无法继续:${pending.map(getStageLabel).join(', ')} 的依赖尚未完成`); + addAgentMessage('assistant', '⚠️ ' + t('后台批次无法继续:') + pending.map(getStageLabel).join(', ') + t(' 的依赖尚未完成')); pending.forEach(stageId => { localStatus[stageId] = 'failed'; failed++; @@ -21990,14 +21990,14 @@ compare: [[flesh, peel]]`; localStatus[stageId] = 'running'; const stage = stageMap[stageId] || stageId; const stageName = getStageName(stage); - addAgentMessage('assistant', `⚙️ 后台执行阶段: ${stageName}...`); + addAgentMessage('assistant', t('⚙️ 后台执行阶段: ') + stageName + '...'); try { const stageParams = (plan.stages_detail || []).find(s => s.id === stageId)?.params || plan.params || {}; await executeStageDirect(stageId, stageParams); localStatus[stageId] = 'completed'; completed++; - addAgentMessage('assistant', `✅ 后台阶段 ${stageName} 完成`); + addAgentMessage('assistant', t('✅ 后台阶段 ') + stageName + t(' 完成')); } catch (err) { localStatus[stageId] = 'failed'; failed++; @@ -22006,7 +22006,7 @@ compare: [[flesh, peel]]`; })); } - addAgentMessage('assistant', `📦 后台批次完成:成功 ${completed}/${stageIds.length},失败 ${failed}/${stageIds.length}`); + addAgentMessage('assistant', t('📦 后台批次完成:成功 ') + completed + '/' + stageIds.length + t(',失败 ') + failed + '/' + stageIds.length); await refreshResults(); }; @@ -22439,9 +22439,9 @@ compare: [[flesh, peel]]`; const reviewPrefix = requestedSpecies ? `${requestedSpecies}.${familyForReview}` : ''; const opened = await maybeShowSubfamilyReview('reference_classification', true, reviewPrefix); if (opened) { - addAgentMessage('assistant', `已调出${requestedSpecies ? requestedSpecies + ' ' : ''}亚家族分类确认表。你可以在弹窗中修订 confirmed_subfamily,点击“确认亚家族标签”后保存。`); + addAgentMessage('assistant', t('已调出') + (requestedSpecies ? requestedSpecies + ' ' : '') + t('亚家族分类确认表。你可以在弹窗中修订 confirmed_subfamily,点击”确认亚家族标签”后保存。')); } else { - addAgentMessage('assistant', `${requestedSpecies ? requestedSpecies + ' ' : ''}亚家族分类确认表没有打开:未找到可确认的分类结果。请先确认该物种已完成 reference_classification。`); + addAgentMessage('assistant', (requestedSpecies ? requestedSpecies + ' ' : '') + t('亚家族分类确认表没有打开:未找到可确认的分类结果。请先确认该物种已完成 reference_classification。')); } nlInput.value = ''; nlLoading.value = false; @@ -22458,7 +22458,7 @@ compare: [[flesh, peel]]`; try { await answerLightweightExtensionHelp(userInput); } catch (err) { - await addAgentMessage('assistant', `拓展分析帮助暂时不可用:${err.message || err}`); + await addAgentMessage('assistant', t('拓展分析帮助暂时不可用:') + (err.message || err)); } finally { nlInput.value = ''; nlLoading.value = false; @@ -22480,8 +22480,8 @@ compare: [[flesh, peel]]`; keepCurrentRows: !wantsAutofillRegenerate, }); addAgentMessage('assistant', wantsAutofillRegenerate - ? '已重新识别并生成 config.xlsx 草稿。请在弹窗中核对并修订,确认后才会写入正式 config.xlsx。' - : '已打开 config.xlsx 自动识别表。你可以直接修订单行,或用批量修订区筛选、预览、Apply。'); + ? t('已重新识别并生成 config.xlsx 草稿。请在弹窗中核对并修订,确认后才会写入正式 config.xlsx。') + : t('已打开 config.xlsx 自动识别表。你可以直接修订单行,或用批量修订区筛选、预览、Apply。')); nlInput.value = ''; nlLoading.value = false; return; @@ -22497,8 +22497,8 @@ compare: [[flesh, peel]]`; keepCurrentRows: !wantsAutofillRegenerate, }); addAgentMessage('assistant', wantsAutofillRegenerate - ? '已重新识别并生成 samples.yaml 草稿。请确认 run、sample_id、condition、replicate 和 control_group 后再写入正式 samples.yaml。' - : '已打开 samples.yaml 自动识别表。你可以直接修订样本、分组、重复和对照组。'); + ? t('已重新识别并生成 samples.yaml 草稿。请确认 run、sample_id、condition、replicate 和 control_group 后再写入正式 samples.yaml。') + : t('已打开 samples.yaml 自动识别表。你可以直接修订样本、分组、重复和对照组。')); nlInput.value = ''; nlLoading.value = false; return; @@ -22527,9 +22527,9 @@ compare: [[flesh, peel]]`; if (autofillAutoQueue.value.length > beforeQueueLength || autofillDialogVisible.value || hasSamplesDraft || hasConfigDraft) { const msg = hasSamplesDraft ? (hasConfigDraft - ? '已识别到 RNA-seq FASTQ 和参考资源,优先打开 samples.yaml 草稿供你核对样本、分组和对照;config.xlsx 草稿也已生成。' - : '已识别到 RNA-seq/FASTQ 数据并生成 samples.yaml 草稿。请在弹窗中核对 run、sample_id、condition、replicate、control_group 后再写入正式 samples.yaml。') - : '已把目录中的可识别参考资源导入当前 job,并生成 config.xlsx 草稿。请在弹窗中核对和修订,点击“确认并写入正式配置”后再执行正式分析。'; + ? t('已识别到 RNA-seq FASTQ 和参考资源,优先打开 samples.yaml 草稿供你核对样本、分组和对照;config.xlsx 草稿也已生成。') + : t('已识别到 RNA-seq/FASTQ 数据并生成 samples.yaml 草稿。请在弹窗中核对 run、sample_id、condition、replicate、control_group 后再写入正式 samples.yaml。')) + : t('已把目录中的可识别参考资源导入当前 job,并生成 config.xlsx 草稿。请在弹窗中核对和修订,点击”确认并写入正式配置”后再执行正式分析。'); addAgentMessage('assistant', msg); nlInput.value = ''; nlLoading.value = false; @@ -22560,7 +22560,7 @@ compare: [[flesh, peel]]`; if (data.action === 'prepare_inputs') { inputPath.value = (data.input_paths || []).join(','); - addAgentMessage('assistant', data.intent.message || '请先核对 config.xlsx 草稿。', { + addAgentMessage('assistant', data.intent.message || t('请先核对 config.xlsx 草稿。'), { stage: null, taskType: 'input_preparation', params: { input_paths: data.input_paths || [] } @@ -22588,7 +22588,7 @@ compare: [[flesh, peel]]`; return; } if (data.action === 'reply' || data.action === 'clarify') { - addAgentMessage('assistant', data.intent.message || '请补充你的需求。', { + addAgentMessage('assistant', data.intent.message || t('请补充你的需求。'), { stage: null, taskType: data.intent.taskType || (data.action === 'clarify' ? 'clarification' : 'faq'), params: {}, @@ -22609,7 +22609,7 @@ compare: [[flesh, peel]]`; if (data.intent.taskType === 'result_view') { autoDetectedMode.value = 'analysis'; if (jobInfo.job_id) await refreshResults(jobInfo.job_id); - addAgentMessage('assistant', data.intent.message || '没有找到可展示的结果。', { + addAgentMessage('assistant', data.intent.message || t('没有找到可展示的结果。'), { stage: null, taskType: 'result_view', params: data.intent.params || {}, @@ -22684,12 +22684,12 @@ compare: [[flesh, peel]]`; setResultStageFilter(requestedCompletedStage); await refreshResults(jobInfo.job_id, requestedCompletedStage); const completedLabel = getStageLabel(requestedCompletedStage); - addAgentMessage('assistant', `${completedLabel} 已有同一分析范围的有效结果,无需重复执行。`, { + addAgentMessage('assistant', completedLabel + t(' 已有同一分析范围的有效结果,无需重复执行。'), { taskType: 'analysis_completed', plan: plan }); } else { - addAgentMessage('assistant', `未生成可执行阶段。未检测到当前 scope 的完成结果时,不再把空计划解释为已完成;请检查阶段名称或前置依赖。`, { + addAgentMessage('assistant', t('未生成可执行阶段。未检测到当前 scope 的完成结果时,不再把空计划解释为已完成;请检查阶段名称或前置依赖。'), { taskType: 'analysis_plan_empty', plan: plan, completed_stages: [...completedSet] @@ -22732,7 +22732,7 @@ compare: [[flesh, peel]]`; String(getStageId(stage) || '').startsWith('wgs_') ); if (containsWgsStage) { - addAgentMessage('assistant', '⏸️ 当前已有前台批次执行中;WGS 规划已保留,请在当前批次结束后重新执行。'); + addAgentMessage('assistant', t('⏸️ 当前已有前台批次执行中;WGS 规划已保留,请在当前批次结束后重新执行。')); nlLoading.value = false; nlInput.value = ''; return; @@ -22762,7 +22762,7 @@ compare: [[flesh, peel]]`; await loadInterspeciesSpeciesOptions(); } batchExecuteDialogVisible.value = true; - addAgentMessage('assistant', `⚙️ 准备执行 ${activeExecutionStages.length} 个分析阶段,请确认参数`); + addAgentMessage('assistant', t('⚙️ 准备执行 ') + activeExecutionStages.length + t(' 个分析阶段,请确认参数')); // 展示规划与参数弹窗,等待用户手动确认。 startBatchExecuteTimer(); @@ -22781,7 +22781,7 @@ compare: [[flesh, peel]]`; const suggested = (planData.suggested_stages || []).map(getStageLabel).join('、'); planHint = `${issueText ? '\n\n检测结果:\n' + issueText : ''}${suggested ? '\n\n建议先执行:' + suggested : ''}${planData.plain_phylogeny_hint ? '\n\n' + planData.plain_phylogeny_hint : ''}`; } - addAgentMessage('assistant', `❌ 无法执行分析:${errorMsg}${planHint}`, { + addAgentMessage('assistant', t('❌ 无法执行分析:') + errorMsg + planHint, { taskType: subfamilyGateStatuses.includes(planData.status) ? 'subfamily_decision' : 'error', diagnosis: planData.context_gate || null, suggestedStages: planData.suggested_stages || [] @@ -22795,7 +22795,7 @@ compare: [[flesh, peel]]`; // API调用异常,显示错误并停止 const errorMsg = planErr.message || "规划请求失败"; ElMessage.error(`❌ ${errorMsg}`); - addAgentMessage('assistant', `❌ 服务暂时不可用:${errorMsg}\n\n请稍后重试。`, { + addAgentMessage('assistant', t('❌ 服务暂时不可用:') + errorMsg + '\n\n' + t('请稍后重试。'), { taskType: 'error' }); nlLoading.value = false; @@ -22803,14 +22803,14 @@ compare: [[flesh, peel]]`; return; } } else { - addAgentMessage('assistant', data.message || '抱歉,我没有理解你的意思。请尝试更详细地描述。'); + addAgentMessage('assistant', data.message || t('抱歉,我没有理解你的意思。请尝试更详细地描述。')); } } catch (err) { if (err?.code === 'user_cancelled') { - addAgentMessage('assistant', '已停止等待,本次未继续获取回答。'); + addAgentMessage('assistant', t('已停止等待,本次未继续获取回答。')); } else { ElMessage.error('请求失败: ' + err.message); - addAgentMessage('assistant', '抱歉,服务暂时不可用,请稍后重试。'); + addAgentMessage('assistant', t('抱歉,服务暂时不可用,请稍后重试。')); } } finally { nlInput.value = ''; diff --git a/apps/web/vue3/static/i18n_en.js b/apps/web/vue3/static/i18n_en.js index 416f57655..41947fdea 100644 --- a/apps/web/vue3/static/i18n_en.js +++ b/apps/web/vue3/static/i18n_en.js @@ -1,3 +1,7 @@ +// ===== Agent 多语言:卡片 + Agent 消息英文翻译 ===== +// key 为中文原文,value 为英文翻译 +// 使用 t(key) 获取翻译,tp(key, params) 处理动态占位符 + window.__I18N_EN__ = { "Agent状态": "AgentStatus", "Self-RAG反思": "Self-RAG Reflection", @@ -375,6 +379,339 @@ window.__I18N_EN__ = { "亚家族确认 - Reference Classification": "Subfamily Confirmation - Reference Classification", "这会停止当前 profile 的 Celery worker 并重新启动。若有任务正在运行,后端会默认拒绝执行。": "This will stop and restart the Celery worker for the current profile. Running tasks will be rejected by default.", "Celery 已重启并完成队列检查": "Celery restarted and queue check complete", - "Celery 重启未完成": "Celery restart incomplete" + "Celery 重启未完成": "Celery restart incomplete", + // ---- Card status_label (8) ---- + "失败": "Failed", + "运行中": "Running", + "待确认": "Pending", + "待执行": "Pending", + "待核对": "Review", + "待授权": "Authorize", + "已取消": "Cancelled", + // ---- Card title (10) ---- + "拓展分析候选核对": "Extension Candidate Review", + "拓展分析说明生成失败": "Description Generation Failed", + "正在生成拓展分析": "Generating Extension", + "运行结果核对": "Result Review", + "输入文件和参数": "Input Files & Parameters", + "正在恢复拓展分析状态": "Restoring Extension State", + // ---- Card purpose/summary (15) ---- + "原始命令、测试数据和执行事实已经保留;这里只重新生成说明,不会重新运行原始命令。": "Raw command, test data and execution facts are preserved; only regenerate description without re-running.", + "可以直接重试,或取消后检查错误原因。": "Retry or cancel and check error cause.", + "这张卡用于核对系统从真实执行中锁定的命令、输入、参数和输出。": "Review system-locked command, inputs, parameters and outputs from real execution.", + "系统正在确定执行入口,并运行上传的代码和测试数据,提取不可修改的执行事实。": "Determining entry point, running uploaded code/test data, extracting immutable execution facts.", + "原始包可复现检查中…": "Checking reproducibility of original package…", + "这张卡用于用同一份测试数据执行已确认的工具,并生成可核对的结果。": "Run confirmed tool with same test data; generate verifiable results.", + "确认无误后点击“开始验证”。": "Click \"Start Validation\" after confirming.", + "这张卡用于查看实际命令、输出与日志;核对无误后才允许授权发布。": "View actual command, output and logs; publication requires review confirmation.", + "这张卡用于确认将已经验证的工具发布为可复用的拓展分析。": "Confirm publishing validated tool as reusable extension.", + "确认后才会发布;不会自动发布。": "Publication requires explicit confirmation; not automatic.", + "这张卡用于确认本次新数据运行的输入文件和原始命令已有参数;确认后立即执行,不会再次弹出确认。": "Confirm input files and parameters for this run; executes immediately without re-confirmation.", + "这张卡展示本次新数据运行生成的文件、标准输出和日志。": "Files, stdout and logs generated by this run.", + "页面已恢复;系统继续轮询同一个操作,不会重复提交。": "Page restored; continuing to poll the same operation without re-submitting.", + "请在页面下方的主对话输入框说明需要修订的内容,然后发送。命令和真实执行事实不会被修改。": "Describe revisions in the main chat input below. Commands and execution facts are immutable.", + "已取消;没有提交后续执行。": "Cancelled; no further execution submitted.", + // ---- Card rows labels (13) ---- + "实际命令": "Actual Command", + "输出文件": "Output Files", + "标准输出": "Standard Output", + "见执行日志": "See execution log", + "功能": "Function", + "输入文件": "Input Files", + "可改参数": "Configurable Parameters", + "标准输出(直接展示)": "Stdout (direct display)", + "(无文件输出)": "(No file output)", + "(无)": "(None)", + // ---- Card error/misc (25+) ---- + "操作失败": "Operation failed", + "读取拓展分析说明失败": "Failed to read extension help", + "读取拓展分析状态失败": "Failed to read extension status", + "阶段:": "Phase: ", + "原因:": "Cause: ", + "无法读取操作状态": "Cannot read operation status", + "提交失败": "Submission failed", + "原始包可复现检查提交失败": "Reproducibility check submission failed", + "阶段:upload": "Phase: Upload", + "已恢复修订;请核对最新候选。": "Revision restored; please review latest candidate.", + "✓ 语义说明已重新生成;执行命令未重新运行。": "✓ Description regenerated; command not re-run.", + "✓ 执行入口已成功复现;候选信息已生成。": "✓ Entry point reproduced successfully; candidate info generated.", + "✓ 验证已完成;结果核对卡已生成。": "✓ Validation complete; result review card generated.", + "✓ 已核对运行结果。": "✓ Results reviewed.", + "✓ 已显式授权并发布。": "✓ Explicitly authorized and published.", + "✓ 已确认输入与参数;本次运行已完成。": "✓ Inputs and parameters confirmed; run completed.", + "✓ 已恢复并完成;候选核对卡已生成。": "✓ Restored and completed; candidate review card generated.", + "✓ 已恢复确认;可以开始验证。": "✓ Confirmation restored; ready to validate.", + "✓ 已恢复验证;结果核对卡已生成。": "✓ Validation restored; result review card generated.", + "✓ 已恢复结果核对;可以授权发布。": "✓ Review restored; ready to publish.", + "✓ 已恢复并发布。": "✓ Restored and published.", + "✓ 已恢复运行;本次结果卡已生成。": "✓ Run restored; result card generated.", + "✓ 已确认候选|未作修订": "✓ Candidate confirmed | no revision", + "✓ 已确认候选|已修订:": "✓ Candidate confirmed | revised: ", + "文件上传成功,已开始生成并验收": "Upload successful; generation and validation started", + "请填写名称并提供测试数据文件或服务器路径;原始命令和原始代码至少提供一项。": "Please provide name and test data files or server path; at least command or code is required.", + "请写明修订意见。": "Please specify revision feedback.", + "读取验证结果失败": "Failed to read validation results", + "读取运行说明失败": "Failed to read run help", + "请先选择实际运行 Job。": "Please select a job first.", + "请填写每个输入文件。": "Please fill in each input file.", + "已修订:": "Revised: ", + "正在恢复 ": "Restoring ", + "…": "…", + " 提交失败": " submission failed", + " 原始包可复现检查提交失败": " Reproducibility check submission failed", + "执行入口已在隔离环境运行。请核对系统记录的事实与说明;确认后才会验证。": "Entry point ran in isolated environment. Review system-recorded facts and descriptions; validation proceeds after confirmation.", + // ---- Agent message: status/confirm/cancel (10) ---- + "config.xlsx 已确认,继续当前目标:": "config.xlsx confirmed; continuing current goal: ", + "✅ 已确认使用拟南芥作为 PPI/KaKs 的显式兜底参考;该选择只写入 Ortholog/PPI/KaKs 专用参数,其它常规阶段不受影响。": "✅ Arabidopsis confirmed as PPI/KaKs explicit fallback reference; affects only Ortholog/PPI/KaKs parameters.", + "✅ 已确认自动注释,正在重新提交": "✅ Auto-annotation confirmed; re-submitting", + "✅ 已选择项目参考物种": "✅ Project reference species selected", + "已停止等待,本次未继续获取回答。": "Stopped waiting; no further response.", + "抱歉,我没有理解你的意思。请尝试更详细地描述。": "Sorry, I didn't understand. Please try describing in more detail.", + "抱歉,服务暂时不可用,请稍后重试。": "Sorry, service temporarily unavailable. Please try again later.", + // ---- Agent message: pause/block/skip (13) ---- + "⏸️ PPI/KaKs 前置数据检查未通过,已暂停批量执行;请补齐参考注释包或确认参考物种后重新执行。": "⏸️ PPI/KaKs prerequisite check failed; batch paused. Add reference annotation or confirm reference species.", + "⏸️ identify 已完成,但方法学链路需要先确认基因家族成员表;保存成员确认后再继续 phylogeny。": "⏸️ identify completed; methodology chain requires confirmed gene family members before continuing phylogeny.", + "⏸️ 已打开 identify 成员初筛确认表;保存后会生成 curated_members,再继续亚家族证据阶段。": "⏸️ identify member confirmation table opened; save to generate curated_members, then continue subfamily evidence stages.", + "⏸️ 当前已有前台批次执行中;WGS 规划已保留,请在当前批次结束后重新执行。": "⏸️ Foreground batch in progress; WGS plan saved. Re-run after current batch finishes.", + ",其它阶段继续执行。": ", other stages continue.", + "⏸️ 已按选择先暂停 Ortholog/PPI/KaKs,其它阶段继续执行。": "⏸️ Paused Ortholog/PPI/KaKs as selected; other stages continue.", + "⏹️ 后端预检失败,当前批次已停止;请重新拉取规划后继续。": "⏹️ Backend pre-check failed; batch stopped. Re-pull plan to continue.", + // ---- Agent message: replan/continue (5) ---- + "🔁 亚家族标签已确认,正在重新规划确认后常规基因家族分析...": "🔁 Subfamily labels confirmed; re-planning post-confirmation gene family analysis...", + "确认后常规分析没有新的可执行阶段。": "No new executable stages after confirmation.", + // ---- Agent message: submit/execute (7) ---- + "⚙️ 提交阶段: ": "⚙️ Submitting stage: ", + "按 per_species 拆分 ": "Split by per_species ", + // ---- Agent message: complete/success (7) ---- + "✅ PPI/KaKs 前置数据检查通过:当前批次已有 Ortholog,将先生成同源映射再继续下游分析。": "✅ PPI/KaKs check passed: Ortholog exists in current batch; generating mapping first.", + // ---- Agent message: download/background (5) ---- + "⬇️ 正在提交推荐近源参考物种注释包后台下载;Ortholog/PPI/KaKs 将等待参考数据,其它阶段继续执行。": "⬇️ Submitting reference annotation download; Ortholog/PPI/KaKs wait for data, other stages continue.", + // ---- Agent message: autofill (2) ---- + "已根据上传文件生成 samples.yaml 草稿。请在弹窗中核对 run、sample_id、condition、replicate、链特异性和分组角色,确认后再写入正式 samples.yaml。": "samples.yaml draft generated. Review run, sample_id, condition, replicate, strand specificity and group roles in the dialog, then confirm to write.", + "已根据上传文件生成 config.xlsx 草稿。请在弹窗中核对物种代码、文件类型、物种角色和 Gene_Family,确认后再写入正式 config.xlsx。": "config.xlsx draft generated. Review species code, file type, species role and Gene_Family in the dialog, then confirm to write.", + // ---- Agent message: methodology (4) ---- + "已保存方法学方案到当前 job。下一步请上传/填写目标物种和参考物种数据,并确认 config.xlsx 后再执行方法学流程。": "Methodology plan saved. Next: upload target/reference species data, confirm config.xlsx, then execute.", + "❌ 方法学执行规划失败:": "❌ Methodology execution planning failed: ", + // ---- Agent message: replot/revision (4 to fix) ---- + "进化树图重绘已提交:": "Phylogeny tree redraw submitted: ", + "进化树图重绘失败:": "Phylogeny tree redraw failed: ", + "Combo 图修订已提交:": "Combo plot revision submitted: ", + "Combo 图修订失败:": "Combo plot revision failed: ", + // ---- Agent message: failure/interrupt (10) ---- + "无法执行分析:": "Cannot execute analysis: ", + "服务暂时不可用:": "Service temporarily unavailable: ", + "⚠️ 批量执行完成:成功 ": "⚠️ Batch complete: success ", + "⚠️ 以下阶段因依赖失败被跳过: ": "⚠️ Stages skipped due to dependency failure: ", + "⚠️ 以下阶段等待上游暂停项处理后继续: ": "⚠️ Stages waiting for upstream paused items: ", + "未生成可执行阶段。未检测到当前 scope 的完成结果时,不再把空计划解释为已完成;请检查阶段名称或前置依赖。": "No executable stages generated. Empty plan is not interpreted as completed. Check stage names or dependencies.", + // ---- Agent message: extension analysis (6) ---- + "拓展分析阶段:": "Extension stage: ", + "拓展分析阶段失败:": "Extension stage failed: ", + "已执行拓展分析阶段:": "Extension stage executed: ", + "拓展分析阶段执行失败:": "Extension stage execution failed: ", + "拓展分析帮助暂时不可用:": "Extension help temporarily unavailable: ", + "上传可执行代码、测试数据和原始命令,核对候选后验证并授权发布;发布后可指定输入和原命令已有参数运行。": "Upload executable code, test data and original command; review candidate, validate, then authorize publishing.", + // ---- Agent message: other (5) ---- + "📌 当前已有前台批次执行中,新任务已转为后台执行:": "📌 Foreground batch running; new task moved to background: ", + "已识别到 RNA-seq/FASTQ 和参考资源": "RNA-seq/FASTQ and reference resources detected", + "已把目录中的可识别参考资源导入当前 job": "Reference resources imported into current job", + "我识别到你是在提供数据路径,但该路径下没有发现": "Data path detected but no usable files found", + "已选择:": "Selected: ", + // ---- Agent message: remaining dynamic patterns ---- + "❌ 批量执行已取消": "❌ Batch execution cancelled", + "⏭️ 跳过阶段: ": "⏭️ Skipped stage: ", + "🚀 准备提交依赖驱动执行,共 ": "🚀 Preparing dependency-driven execution: ", + "🎉 批量执行完成!成功: ": "🎉 Batch complete! Success: ", + ",失败 ": ", failed ", + "跳过阶段: ": "Skipped stage: ", + " 暂停:需要先准备参考物种 PEP/CDS 或 Ortholog 映射。": " paused: prepare reference species PEP/CDS or Ortholog mapping first.", + " 已暂停依赖 confirmed_subfamily.tsv 的后续阶段;请在亚家族确认表中保存后重新拉取规划继续执行。": " paused subsequent stages depending on confirmed_subfamily.tsv; save the subfamily confirmation table and re-pull the plan.", + "参考数据已就绪": "Reference data ready", + " 已恢复为待执行,将随当前批次继续调度。": " restored as pending; will continue with current batch.", + " 开始续跑 Ortholog/PPI/KaKs。": " resuming Ortholog/PPI/KaKs.", + " 重新提交(自动注释背景)": " re-submit (auto-annotation background)", + " 已提交后端,task_id=": " submitted to backend, task_id=", + " 准备执行 ": " preparing to execute ", + " 个阶段,请确认参数": " stages; please confirm parameters", + " 个阶段...": " stages...", + " 个分析阶段,请确认参数": " analysis stages; please confirm parameters", + "亚家族确认后已规划 ": "Post-subfamily-confirmation plan: ", + " 个常规分析阶段": " regular analysis stages", + "PPI/KaKs 前置数据检查通过:检测到已下载近源参考 ": "PPI/KaKs check passed: downloaded reference ", + ",将用该参考先生成 Ortholog mapping。": ", generating Ortholog mapping first.", + "PPI/KaKs 前置数据检查通过:检测到已有 Ortholog 映射(": "PPI/KaKs check passed: Ortholog mapping exists (", + "),将复用该映射执行 ": "), reusing mapping for ", + "),将直接执行 ": "), executing directly ", + "),已自动插入 Ortholog,将先生成 mapping 再执行 ": "), Ortholog auto-inserted; generating mapping then executing ", + ",已自动插入 Ortholog": ", Ortholog auto-inserted", + "推荐参考数据下载完成:": "Reference download complete: ", + ")。已写入 Ortholog/PPI/Ka/Ks 专用参考物种。": "). Written to Ortholog/PPI/Ka/Ks specific reference species.", + " 已按用户确认使用 bcftools 完成": " completed with bcftools as confirmed by user", + " 前端连接短暂中断,正在按后端产物状态恢复": " frontend connection interrupted; recovering from backend state", + "亚家族确认已完成,但后端当前 workflowPhase 为 ": "Subfamily confirmation completed; backend workflowPhase is ", + ",未自动打开常规分析。": "; no auto-analysis opened.", + "亚家族分类已完成预测,但确认表尚未保存;": "Subfamily classification predicted; confirmation table not yet saved; ", + "已调出": "Opened ", + "亚家族分类确认表。你可以在弹窗中修订 confirmed_subfamily,点击\"确认亚家族标签\"后保存。": " subfamily classification confirmation table. Revise confirmed_subfamily in the dialog, then click \"Confirm Subfamily Labels\" to save.", + "亚家族分类确认表没有打开:未找到可确认的分类结果。请先确认该物种已完成 reference_classification。": " subfamily classification confirmation table not opened: no confirmable results found. Confirm the species has completed reference_classification.", + "确认后常规分析没有新的可执行阶段。": "No new executable stages after confirmation.", + "⚠️ 检测到 ": "⚠️ Detected ", + ",将在确认后启动本地自动注释。": "; auto-annotation will start after confirmation.", + "🧬 自动背景注释与 ": "🧬 Auto-background annotation and ", + " 已提交后端,task_id=": " submitted to backend, task_id=", + "⚠️ 亚家族确认后自动续跑规划失败:": "⚠️ Post-subfamily-confirm auto-plan failed: ", + "; Ortholog/PPI/KaKs ": "; Ortholog/PPI/KaKs ", + "推荐参考数据下载已转入后台:": "Reference download moved to background: ", + ",task_id=": ", task_id=", + "后台批次完成:成功 ": "Background batch complete: success ", + "后台执行阶段: ": "Background stage: ", + "后台阶段 ": "Background stage ", + " 完成": " done", + "已保存方法学方案,但暂不执行:": "Methodology plan saved, not executing: ", + "。请在 config.xlsx 自动识别弹窗中核对/补齐数据,确认写入正式配置后再点击\"确认并执行\"。": ". Review/fill data in the config.xlsx auto-detect dialog, confirm to write then click \"Confirm & Execute\".", + "当前已有前台批次执行中;WGS 规划已保留,请在当前批次结束后重新执行。": "Foreground batch in progress; WGS plan saved. Re-run after current batch finishes.", + "拓展分析阶段执行失败:": "Extension stage execution failed: ", + "已把路径中的 ": "Soft-linked ", + " 个可识别文件软链接到当前 job": " recognizable files to current job", + " 个可识别文件软链接到当前 job,但没有发现需要自动填写 config.xlsx 或 samples.yaml 的文件类型;请检查目录或继续补充文件。": " recognizable files linked to current job; no files found for config.xlsx/samples.yaml auto-fill. Check directory or add more files.", + "前": " ago", + "秒前": "sec ago", + "分钟前": "min ago", + "小时前": "hr ago", + "天前": "d ago", + "待机": "Idle", + "分析中": "Analyzing", + "检索中": "Retrieving", + "反思中": "Reflecting", + "生成中": "Generating", + "完成": "Complete", + "错误": "Error", + "处理中...": "Processing...", + "发送": "Send", + "任务列表": "Tasks", + " 个常规分析阶段,请确认参数": " regular analysis stages; please confirm parameters", + " 亚家族分类已完成预测,但确认表尚未保存;后续会继续显示为待确认。": " subfamily classification predicted; confirmation table not yet saved; will show as pending.", + " 到后端;系统会先生成 ": " to backend; system will generate ", + " 前端连接短暂中断,正在按后端产物状态恢复,不会立即判定阶段失败": " frontend connection interrupted; recovering from backend artifact state. Stage will not be marked as failed.", + " 已有同一分析范围的有效结果,无需重复执行。": " already has valid results for the same analysis scope. No need to re-execute.", + " 的依赖尚未完成": " dependency not yet completed", + " 重新提交(自动注释背景)...": " re-submitting (auto-annotation background)...", + " 提交失败": " submission failed", + " 原始包可复现检查提交失败": " reproducibility check submission failed", + "Combo 图修订已提交:task_id=": "Combo plot revision submitted: task_id=", + "⏳ 推荐参考数据下载已转入后台:": "⏳ Reference download moved to background: ", + "⏸️ 以下阶段等待上游暂停项处理后继续: ": "⏸️ Stages waiting for upstream paused items: ", + "⚙️ 亚家族确认后已规划 ": "⚙️ Post-subfamily-confirm plan: ", + "⚙️ 准备执行 ": "⚙️ Preparing to execute ", + "⚙️ 后台执行阶段: ": "⚙️ Background stage: ", + "⚠️ 后台批次中以下阶段因依赖失败被跳过: ": "⚠️ Background batch stages skipped due to dependency failure: ", + "✅ PPI/KaKs 前置数据检查通过:检测到已下载近源参考 ": "✅ PPI/KaKs check passed: downloaded reference ", + "✅ PPI/KaKs 前置数据检查通过:检测到已有 Ortholog 映射(": "✅ PPI/KaKs check passed: Ortholog mapping found (", + "✅ 后台阶段 ": "✅ Background stage ", + "✅ 已确认自动注释,正在重新提交 ": "✅ Auto-annotation confirmed; re-submitting ", + "✅ 已选择项目参考物种 ": "✅ Selected project reference species: ", + "✅ 推荐参考数据下载完成:": "✅ Reference download complete: ", + "✅ 阶段 ": "✅ Stage ", + "☒ 已恢复并完成。": "☒ Restored and completed.", + "❌ 无法执行分析:": "❌ Cannot execute analysis: ", + "❌ 服务暂时不可用:": "❌ Service temporarily unavailable: ", + "。结果已写入当前 job workspace,输出:": ". Results written to current job workspace, output: ", + "。请在 config.xlsx 自动识别弹窗中核对/补齐数据,确认写入正式配置后再点击“确认并执行”。": ". Review/fill data in the config.xlsx auto-detect dialog; confirm to write then click \"Confirm & Execute\".", + "亚家族分类确认表。你可以在弹窗中修订 confirmed_subfamily,点击“确认亚家族标签”后保存。": "subfamily classification confirmation table. Revise confirmed_subfamily in the dialog, then click \"Confirm Subfamily Labels\" to save.", + "亚家族标签确认被跳过,已停止依赖 confirmed_subfamily.tsv 的后续阶段;如需普通无亚家族标签分析,请重新发起并明确说明\"不需要亚家族\"。": "Subfamily label confirmation skipped; subsequent stages depending on confirmed_subfamily.tsv have been stopped. For regular analysis without subfamily labels, please restart and explicitly state \"no subfamily\".", + "发送\"按已保存的方法学方案继续执行\"": "Send \"Continue with saved methodology plan\"", + "后台批次无法继续:": "Background batch cannot continue: ", + "已打开 config.xlsx 自动识别表。你可以直接修订单行,或用批量修订区筛选、预览、Apply。": "Opened config.xlsx auto-identification table. You can edit individual rows, or use the batch revision area to filter, preview, and Apply.", + "已打开 samples.yaml 自动识别表。你可以直接修订样本、分组、重复和对照组。": "Opened samples.yaml auto-identification table. You can revise samples, groups, replicates and controls directly.", + "已把目录中的可识别参考资源导入当前 job,并生成 config.xlsx 草稿。请在弹窗中核对和修订,点击“确认并写入正式配置”后再执行正式分析。": "Imported recognizable reference resources into current job and generated config.xlsx draft. Please review and revise in the dialog, then click \"Confirm & Write to Official Config\" before executing.", + "已识别到 RNA-seq FASTQ 和参考资源,优先打开 samples.yaml 草稿供你核对样本、分组和对照;config.xlsx 草稿也已生成。": "RNA-seq FASTQ and reference resources detected; opening samples.yaml draft for sample/group/control review. config.xlsx draft also generated.", + "已识别到 RNA-seq/FASTQ 数据并生成 samples.yaml 草稿。请在弹窗中核对 run、sample_id、condition、replicate、control_group 后再写入正式 samples.yaml。": "RNA-seq/FASTQ data detected and samples.yaml draft generated. Review run, sample_id, condition, replicate, control_group in the dialog, then confirm to write.", + "已重新识别并生成 config.xlsx 草稿。请在弹窗中核对并修订,确认后才会写入正式 config.xlsx。": "Re-identified and generated config.xlsx draft. Review and revise in the dialog; writes to official config.xlsx only after confirmation.", + "已重新识别并生成 samples.yaml 草稿。请确认 run、sample_id、condition、replicate 和 control_group 后再写入正式 samples.yaml。": "Re-identified and generated samples.yaml draft. Confirm run, sample_id, condition, replicate, and control_group before writing to official samples.yaml.", + "没有找到可展示的结果。": "No displayable results found.", + "点击\"浏览\"上传文献": "Click \"Browse\" to upload paper", + "点击\"知识图谱\"查看关系": "Click \"Knowledge Graph\" to view relations", + "请先核对 config.xlsx 草稿。": "Please review the config.xlsx draft first.", + "请补充你的需求。": "Please clarify your requirements.", + "进化树图重绘已提交:task_id=": "Phylogeny tree redraw submitted: task_id=", + "选择输入框上方\"Analysis\"": "Select \"Analysis\" above the input box", + "选择输入框上方\"RAG\"": "Select \"RAG\" above the input box", + ",再继续富集分析。": ", then continue enrichment analysis.", + ",将用该参考先生成 Ortholog mapping。": ", will generate Ortholog mapping from this reference first.", + ";Ortholog/PPI/KaKs 已恢复为待执行,将随当前批次继续调度。": "; Ortholog/PPI/KaKs restored as pending; will continue with current batch.", + ",将按该参考检查或生成 Ortholog,并执行 ": "; will check or generate Ortholog from this reference, and execute ", + ";开始续跑 Ortholog/PPI/KaKs。": "; resuming Ortholog/PPI/KaKs.", + "),将复用该映射执行 ": "), will reuse this mapping and execute ", + "),将直接执行 ": "), will execute directly ", + ",已自动插入 Ortholog,将先生成 mapping 再执行 ": ", Ortholog auto-inserted; generating mapping first then executing ", + ")。已写入 Ortholog/PPI/Ka/Ks 专用参考物种。": "). Written to Ortholog/PPI/Ka/Ks specific reference species.", + " 已按用户确认使用 bcftools 完成": " completed with bcftools as user confirmed", + " 已暂停依赖 confirmed_subfamily.tsv 的后续阶段;请在亚家族确认表中保存后重新拉取规划继续执行。": " has paused subsequent stages depending on confirmed_subfamily.tsv; save the subfamily confirmation table and re-pull the plan.", + " 暂停:需要先准备参考物种 PEP/CDS 或 Ortholog 映射。": "paused: prepare reference species PEP/CDS or Ortholog mapping first.", + " 个分析阶段,请确认参数": " analysis stages, please confirm parameters", + "📦 后台批次完成:成功 ": "📦 Background batch complete: success ", + "🔄 阶段 ": "🔄 Stage ", + "🚀 阶段 ": "🚀 Stage ", + "config.xlsx 已确认,继续当前目标:": "config.xlsx confirmed; continuing current goal: ", + "按 per_species 拆分 ": "Split by per_species ", + "⚠️ 检测到 ": "⚠️ Detected ", + ",将在确认后启动本地自动注释。": "; auto-annotation will start after confirmation.", + "。已写入 Ortholog/PPI/Ka/Ks 专用参考物种。": ". Written to Ortholog/PPI/Ka/Ks specific reference species.", + "🚀 准备提交依赖驱动执行,共 ": "🚀 Preparing dependency-driven execution: ", + " 个阶段...": " stages...", + "⚠️ 批量执行完成:成功 ": "⚠️ Batch complete: success ", + ",失败 ": ", failed ", + "🎉 批量执行完成!成功: ": "🎉 Batch complete! Success: ", + "⏭️ 跳过阶段: ": "⏭️ Skipped stage: ", + "⚙️ 提交阶段: ": "⚙️ Submitting stage: ", + "⏭️ ": "⏭️ ", + " 已提交后端,task_id=": " submitted to backend, task_id=", + " 完成": " complete", + ",task_id=": ", task_id=", + "⚠️ 亚家族确认后自动续跑规划失败:": "⚠️ Post-subfamily-confirm auto-plan failed: ", + "亚家族确认已完成,但后端当前 workflowPhase 为 ": "Subfamily confirmation complete; backend workflowPhase is ", + ",未自动打开常规分析。": "; no auto-analysis opened.", + "已调出": "Opened: ", + "亚家族分类确认表没有打开:未找到可确认的分类结果。请先确认该物种已完成 reference_classification。": "Subfamily classification confirmation table not opened: no confirmable results found. Confirm the species has completed reference_classification.", + "拓展分析帮助暂时不可用:": "Extension help temporarily unavailable: ", + "已保存方法学方案,但暂不执行:": "Methodology plan saved; not executing: ", + "🧬 自动背景注释与 ": "🧬 Auto-background annotation and ", + "参考数据已就绪": "Reference data ready", + "Combo 图修订失败:": "Combo plot revision failed: ", + "进化树图重绘失败:": "Phylogeny tree redraw failed: ", + ",其它阶段继续执行。": ", other stages continue.", + " 个阶段,请确认参数": " stages, please confirm parameters", + "—": "—", + "✓ 已恢复并完成。": "✓ Restored and completed.", + "后台批次完成:成功 ": "Background batch complete: success ", + "后台执行阶段: ": "Background stage: ", + "拓展分析阶段执行失败:": "Extension stage execution failed: ", + "拓展分析阶段:": "Extension stage: ", + "已执行拓展分析阶段:": "Extension stage executed: ", + "拓展分析阶段失败:": "Extension stage failed: ", + "MSA区间重绘已提交": "MSA replot submitted", + "MSA区间重绘失败": "MSA replot failed", + "已选择:": "Selected: ", + "📌 当前已有前台批次执行中,新任务已转为后台执行:": "📌 Foreground batch running; new task moved to background: ", + ",已自动插入 Ortholog,将先生成 mapping 再执行": ", Ortholog auto-inserted; generating mapping first then executing", + "推荐参考数据下载完成:": "Reference download complete: ", + "ABORTED": "ABORTED", + "的依赖尚未完成": "dependency not yet completed", + "亚家族标签确认被跳过,已停止依赖 confirmed_subfamily.tsv 的后续阶段;如需普通无亚家族标签分析,请重新发起并明确说明": "Subfamily label confirmation skipped; stages depending on confirmed_subfamily.tsv stopped. For regular analysis without subfamily labels, restart and explicitly state: ", + "不需要亚家族": "no subfamily", + "。": ".", + ",将复用该映射执行 ": ", will reuse this mapping and execute ", + ";将按该参考检查或生成 Ortholog,并执行 ": "; will check or generate Ortholog from this reference, and execute ", + "⚠️ 以下阶段因依赖失败被跳过: ": "⚠️ Stages skipped due to dependency failure: ", + "⚠️ 以下阶段等待上游暂停项处理后继续: ": "⚠️ Stages waiting for upstream paused items: ", + "启动子自定义绘图已提交": "Promoter custom plot submitted", + "启动子自定义绘图失败": "Promoter custom plot failed", + "种内共线性 Circos 重绘已提交": "Intraspecies Circos replot submitted", + "种内共线性 Circos 重绘失败": "Intraspecies Circos replot failed", + " 重新提交(自动注释背景)": " re-submit (auto-annotation background)", + " 已暂停依赖 confirmed_subfamily.tsv": " has paused stages depending on confirmed_subfamily.tsv", + ";": ";", + " 个分析阶段": " analysis stages", + " 个阶段": " stages" };