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"
};