diff --git a/i18n/en-US.js b/i18n/en-US.js index bde0d22..e09d63d 100644 --- a/i18n/en-US.js +++ b/i18n/en-US.js @@ -11,6 +11,7 @@ export default { "common.delete": "Delete", "common.edit": "Edit", "common.empty": "No data", + "common.emptyParenthetical": "(empty)", "common.loading": "Loading…", "common.retry": "Retry", "common.save": "Save", @@ -197,8 +198,32 @@ export default { "persistence.persistState.pending": "Awaiting persistence confirmation", "recall.card.deleteConfirm": "Delete this persisted recall injection?", + "recall.card.confirmDeleteShort": "Delete?", + "recall.card.empty.graphNotReady": "Graph is not ready", + "recall.card.empty.nodesMissing": "Recalled nodes no longer exist or the graph was rebuilt", + "recall.card.enaNote": "Memory block triggered by Ena Planner for this turn", + "recall.card.injectionPreview": "Injection Preview", + "recall.card.injectionPreview.ena": "ENA Injection Preview", "recall.card.memoryCount": "{count} memories", + "recall.card.memoryReady": "Memories ✓", + "recall.card.meta.authoritativeInput": "Authoritative input", + "recall.card.meta.fallbackCount": "{count} fallback attempts", + "recall.card.meta.manualEdit": "Manual edit", + "recall.card.meta.source": "Source: {source}", + "recall.card.rerun": "Rerun Recall", + "recall.card.rerunning": "Recalling...", "recall.card.title": "Relevant Memory Recall", + "recall.card.userInput": "Current user input", + "recall.sidebar.edgeCount": "{count} edges", + "recall.sidebar.editTitle": "Edit Recall Injection", + "recall.sidebar.injectionEditable": "Injection Text (editable)", + "recall.sidebar.injectionText": "Injection Text", + "recall.sidebar.inputPlaceholder": "Enter injection text...", + "recall.sidebar.nodeImportance": "Importance", + "recall.sidebar.nodeName": "Name", + "recall.sidebar.nodeTitle": "Node Details", + "recall.sidebar.nodeType": "Type", + "recall.sidebar.related": "Related", "stage.extraction": "Extraction", "stage.history": "History Recovery", diff --git a/i18n/zh-CN.js b/i18n/zh-CN.js index e452180..d03670a 100644 --- a/i18n/zh-CN.js +++ b/i18n/zh-CN.js @@ -11,6 +11,7 @@ export default { "common.delete": "删除", "common.edit": "编辑", "common.empty": "暂无数据", + "common.emptyParenthetical": "(空)", "common.loading": "加载中…", "common.retry": "重试", "common.save": "保存", @@ -197,8 +198,32 @@ export default { "persistence.persistState.pending": "等待正式持久化确认", "recall.card.deleteConfirm": "确认删除这条持久召回注入?", + "recall.card.confirmDeleteShort": "确认删除?", + "recall.card.empty.graphNotReady": "图谱未就绪", + "recall.card.empty.nodesMissing": "召回节点已不存在或图谱已重建", + "recall.card.enaNote": "由 Ena Planner 触发的本轮记忆块", + "recall.card.injectionPreview": "注入预览", + "recall.card.injectionPreview.ena": "ENA 注入预览", "recall.card.memoryCount": "记忆 {count}", + "recall.card.memoryReady": "记忆 ✓", + "recall.card.meta.authoritativeInput": "权威输入", + "recall.card.meta.fallbackCount": "回退 {count} 次", + "recall.card.meta.manualEdit": "手动编辑", + "recall.card.meta.source": "来源: {source}", + "recall.card.rerun": "重新召回", + "recall.card.rerunning": "召回中...", "recall.card.title": "相关记忆召回", + "recall.card.userInput": "本轮用户输入", + "recall.sidebar.edgeCount": "{count} 条边", + "recall.sidebar.editTitle": "编辑召回注入", + "recall.sidebar.injectionEditable": "注入文本(可编辑)", + "recall.sidebar.injectionText": "注入文本", + "recall.sidebar.inputPlaceholder": "输入注入文本...", + "recall.sidebar.nodeImportance": "重要度", + "recall.sidebar.nodeName": "名称", + "recall.sidebar.nodeTitle": "节点详情", + "recall.sidebar.nodeType": "类型", + "recall.sidebar.related": "关联", "stage.extraction": "提取", "stage.history": "历史恢复", diff --git a/ui/recall-message-ui.js b/ui/recall-message-ui.js index 5ecfca7..087cda8 100644 --- a/ui/recall-message-ui.js +++ b/ui/recall-message-ui.js @@ -3,6 +3,7 @@ import { getContext } from "../../../../extensions.js"; import { GraphRenderer } from "./graph-renderer.js"; +import { t } from "../i18n/index.js"; function _hostUserPovAliasHintsForRecallCanvas() { try { @@ -82,12 +83,12 @@ function formatTokenHint(tokenEstimate) { function formatMetaLine(record) { const parts = []; if (record.recallSource && !buildRecallSourceLabel(record)) { - parts.push(`来源: ${record.recallSource}`); + parts.push(t("recall.card.meta.source", { source: record.recallSource })); } - if (record.authoritativeInputUsed) parts.push("权威输入"); + if (record.authoritativeInputUsed) parts.push(t("recall.card.meta.authoritativeInput")); if (record.tokenEstimate > 0) parts.push(`~${record.tokenEstimate} tokens`); if (Number.isFinite(record.generationCount) && record.generationCount > 0) { - parts.push(`回退 ${record.generationCount} 次`); + parts.push(t("recall.card.meta.fallbackCount", { count: record.generationCount })); } if (record.updatedAt) { const dateStr = String(record.updatedAt).replace(/T/, " ").replace(/\.\d+Z$/, ""); @@ -253,7 +254,7 @@ function buildInjectionPreviewBlock(record = {}) { const defaultExpanded = isEna; header.setAttribute("aria-expanded", defaultExpanded ? "true" : "false"); header.innerHTML = ` - ${isEna ? "ENA 注入预览" : "注入预览"} + ${isEna ? t("recall.card.injectionPreview.ena") : t("recall.card.injectionPreview")} `; wrap.appendChild(header); @@ -261,7 +262,7 @@ function buildInjectionPreviewBlock(record = {}) { const content = el("div", "bme-recall-injection-content"); if (isEna) { content.appendChild( - el("div", "bme-recall-injection-note", "由 Ena Planner 触发的本轮记忆块"), + el("div", "bme-recall-injection-note", t("recall.card.enaNote")), ); } appendInjectionPreviewContent(content, injectionText); @@ -341,18 +342,18 @@ export function createRecallCardElement({ // -- 用户消息区 -- const userLabel = el("div", "bme-recall-user-label"); const userLabelText = el("div", "bme-recall-user-label-text"); - userLabelText.innerHTML = "💬 本轮用户输入"; + userLabelText.innerHTML = `💬 ${t("recall.card.userInput")}`; userLabel.appendChild(userLabelText); const userLabelActions = el("div", "bme-recall-user-label-actions"); const editUserInputBtn = el("button", "bme-recall-user-edit-btn"); editUserInputBtn.type = "button"; - editUserInputBtn.innerHTML = '✏️编辑'; + editUserInputBtn.innerHTML = `✏️${t("common.edit")}`; userLabelActions.appendChild(editUserInputBtn); userLabel.appendChild(userLabelActions); card.appendChild(userLabel); - const userText = el("div", "bme-recall-user-text", activeUserMessageText || "(empty)"); + const userText = el("div", "bme-recall-user-text", activeUserMessageText || t("common.emptyParenthetical")); card.appendChild(userText); const userEditWrap = el("div", "bme-recall-user-edit-wrap"); @@ -360,9 +361,9 @@ export function createRecallCardElement({ userEditTextarea.className = "bme-recall-user-edit-textarea"; userEditWrap.appendChild(userEditTextarea); const userEditActions = el("div", "bme-recall-user-edit-actions"); - const userEditSaveBtn = el("button", "bme-recall-user-edit-action primary", "保存"); + const userEditSaveBtn = el("button", "bme-recall-user-edit-action primary", t("common.save")); userEditSaveBtn.type = "button"; - const userEditCancelBtn = el("button", "bme-recall-user-edit-action secondary", "取消"); + const userEditCancelBtn = el("button", "bme-recall-user-edit-action secondary", t("common.cancel")); userEditCancelBtn.type = "button"; userEditActions.appendChild(userEditSaveBtn); userEditActions.appendChild(userEditCancelBtn); @@ -378,13 +379,13 @@ export function createRecallCardElement({ const barIcon = el("span", "bme-recall-bar-icon", "🧠"); bar.appendChild(barIcon); - const barTitle = el("span", "bme-recall-bar-title", "相关记忆召回"); + const barTitle = el("span", "bme-recall-bar-title", t("recall.card.title")); bar.appendChild(barTitle); const badge = el( "span", "bme-recall-count-badge", - initialNodeCount > 0 ? `记忆 ${initialNodeCount}` : "记忆 ✓", + initialNodeCount > 0 ? t("recall.card.memoryCount", { count: initialNodeCount }) : t("recall.card.memoryReady"), ); bar.appendChild(badge); @@ -445,7 +446,7 @@ export function createRecallCardElement({ const emptyMsg = el( "div", "bme-recall-empty", - activeGraph ? "召回节点已不存在或图谱已重建" : "图谱未就绪", + activeGraph ? t("recall.card.empty.nodesMissing") : t("recall.card.empty.graphNotReady"), ); body.appendChild(emptyMsg); } else { @@ -495,7 +496,7 @@ export function createRecallCardElement({ meta.appendChild(el("span", "bme-recall-meta-text", metaText)); } if (activeRecord?.manuallyEdited) { - const tag = el("span", "bme-recall-meta-tag", "✍ 手动编辑"); + const tag = el("span", "bme-recall-meta-tag", `✍ ${t("recall.card.meta.manualEdit")}`); meta.appendChild(tag); } body.appendChild(meta); @@ -509,7 +510,7 @@ export function createRecallCardElement({ const actions = el("div", "bme-recall-actions"); const editBtn = el("button", "bme-recall-action-btn"); - editBtn.innerHTML = '✏️ 编辑'; + editBtn.innerHTML = `✏️ ${t("common.edit")}`; editBtn.type = "button"; editBtn.addEventListener("click", (e) => { e.stopPropagation(); @@ -518,7 +519,7 @@ export function createRecallCardElement({ actions.appendChild(editBtn); const deleteBtn = el("button", "bme-recall-action-btn"); - deleteBtn.innerHTML = '🗑 删除'; + deleteBtn.innerHTML = `🗑 ${t("common.delete")}`; deleteBtn.type = "button"; setupDeleteConfirmation(deleteBtn, () => { activeCallbacks.onDelete?.(messageIndex); @@ -526,7 +527,7 @@ export function createRecallCardElement({ actions.appendChild(deleteBtn); const recallBtn = el("button", "bme-recall-action-btn"); - recallBtn.innerHTML = '🔄 重新召回'; + recallBtn.innerHTML = `🔄 ${t("recall.card.rerun")}`; recallBtn.type = "button"; recallBtn.addEventListener("click", async (e) => { e.stopPropagation(); @@ -578,7 +579,7 @@ export function createRecallCardElement({ "bme-recall-hide-user-input", activeUserInputDisplayMode === "off", ); - userText.textContent = activeUserMessageText || "(empty)"; + userText.textContent = activeUserMessageText || t("common.emptyParenthetical"); if (isEditingUserInput) { userEditTextarea.value = activeUserMessageText || ""; } @@ -586,7 +587,7 @@ export function createRecallCardElement({ const nodeCount = Array.isArray(activeRecord?.selectedNodeIds) ? activeRecord.selectedNodeIds.length : 0; - badge.textContent = nodeCount > 0 ? `记忆 ${nodeCount}` : "记忆 ✓"; + badge.textContent = nodeCount > 0 ? t("recall.card.memoryCount", { count: nodeCount }) : t("recall.card.memoryReady"); tokenHint.textContent = formatTokenHint(activeRecord?.tokenEstimate); if (skipExpandedRerender || !card.classList.contains("expanded")) return; @@ -627,7 +628,7 @@ export function createRecallCardElement({ if (result?.ok) { if (Object.prototype.hasOwnProperty.call(result, "nextText")) { activeUserMessageText = String(result.nextText || ""); - userText.textContent = activeUserMessageText || "(empty)"; + userText.textContent = activeUserMessageText || t("common.emptyParenthetical"); } setUserInputEditMode(false); } @@ -705,7 +706,7 @@ export function setupDeleteConfirmation(button, onConfirm) { return; } pendingConfirm = true; - button.textContent = "确认删除?"; + button.textContent = t("recall.card.confirmDeleteShort"); button.classList.add("danger"); confirmTimer = setTimeout(reset, DELETE_CONFIRM_TIMEOUT_MS); }); @@ -717,7 +718,7 @@ export function setRecallButtonLoading(button, loading) { if (loading) { button._bmeOriginalHTML = button.innerHTML; button.innerHTML = - ' 召回中...'; + ` ${t("recall.card.rerunning")}`; button.classList.add("loading"); button.disabled = true; } else { @@ -769,7 +770,7 @@ export function openRecallSidebar({ const header = el("div", "bme-recall-sidebar-header"); const headerTitle = el("div", "bme-recall-sidebar-header-title"); headerTitle.textContent = - mode === "edit" ? "📝 编辑召回注入" : "🔍 节点详情"; + mode === "edit" ? `📝 ${t("recall.sidebar.editTitle")}` : `🔍 ${t("recall.sidebar.nodeTitle")}`; header.appendChild(headerTitle); const closeBtn = el("button", "bme-recall-sidebar-close"); @@ -784,9 +785,9 @@ export function openRecallSidebar({ if (node && mode === "view") { const nodeInfo = el("div", "bme-recall-sidebar-node-info"); const rows = [ - ["类型", node.type || node.raw?.type || "-"], - ["名称", node.name || node.raw?.name || "-"], - ["重要度", String(node.importance ?? node.raw?.importance ?? "-")], + [t("recall.sidebar.nodeType"), node.type || node.raw?.type || "-"], + [t("recall.sidebar.nodeName"), node.name || node.raw?.name || "-"], + [t("recall.sidebar.nodeImportance"), String(node.importance ?? node.raw?.importance ?? "-")], ]; for (const [label, value] of rows) { const row = el("div", "bme-recall-sidebar-node-info-row"); @@ -809,8 +810,8 @@ export function openRecallSidebar({ ); if (relatedEdges.length > 0) { const edgeRow = el("div", "bme-recall-sidebar-node-info-row"); - const edgeLabel = el("span", "bme-recall-sidebar-node-info-label", "关联"); - const edgeValue = el("span", "", `${relatedEdges.length} 条边`); + const edgeLabel = el("span", "bme-recall-sidebar-node-info-label", t("recall.sidebar.related")); + const edgeValue = el("span", "", t("recall.sidebar.edgeCount", { count: relatedEdges.length })); edgeRow.appendChild(edgeLabel); edgeRow.appendChild(edgeValue); nodeInfo.appendChild(edgeRow); @@ -825,7 +826,7 @@ export function openRecallSidebar({ const sectionLabel = el( "div", "bme-recall-sidebar-section-label", - mode === "edit" ? "注入文本(可编辑)" : "注入文本", + mode === "edit" ? t("recall.sidebar.injectionEditable") : t("recall.sidebar.injectionText"), ); body.appendChild(sectionLabel); @@ -836,7 +837,7 @@ export function openRecallSidebar({ textarea = document.createElement("textarea"); textarea.className = "bme-recall-sidebar-textarea"; textarea.value = injectionText; - textarea.placeholder = "输入注入文本..."; + textarea.placeholder = t("recall.sidebar.inputPlaceholder"); body.appendChild(textarea); const tokenHint = el("div", "bme-recall-sidebar-token-hint"); @@ -856,7 +857,7 @@ export function openRecallSidebar({ }); body.appendChild(tokenHint); } else { - const readonlyEl = el("div", "bme-recall-sidebar-readonly", injectionText || "(empty)"); + const readonlyEl = el("div", "bme-recall-sidebar-readonly", injectionText || t("common.emptyParenthetical")); body.appendChild(readonlyEl); } @@ -866,7 +867,7 @@ export function openRecallSidebar({ const footer = el("div", "bme-recall-sidebar-footer"); if (mode === "edit") { - const saveBtn = el("button", "bme-recall-sidebar-btn primary", "保存"); + const saveBtn = el("button", "bme-recall-sidebar-btn primary", t("common.save")); saveBtn.type = "button"; saveBtn.addEventListener("click", () => { const newText = textarea?.value || ""; @@ -875,13 +876,13 @@ export function openRecallSidebar({ }); footer.appendChild(saveBtn); - const cancelBtn = el("button", "bme-recall-sidebar-btn secondary", "取消"); + const cancelBtn = el("button", "bme-recall-sidebar-btn secondary", t("common.cancel")); cancelBtn.type = "button"; cancelBtn.addEventListener("click", () => closeRecallSidebar()); footer.appendChild(cancelBtn); } else { // View mode: offer edit button - const editBtn = el("button", "bme-recall-sidebar-btn primary", "✏️ 编辑"); + const editBtn = el("button", "bme-recall-sidebar-btn primary", `✏️ ${t("common.edit")}`); editBtn.type = "button"; editBtn.addEventListener("click", () => { openRecallSidebar({ @@ -895,7 +896,7 @@ export function openRecallSidebar({ }); footer.appendChild(editBtn); - const closeFooterBtn = el("button", "bme-recall-sidebar-btn secondary", "关闭"); + const closeFooterBtn = el("button", "bme-recall-sidebar-btn secondary", t("common.close")); closeFooterBtn.type = "button"; closeFooterBtn.addEventListener("click", () => closeRecallSidebar()); footer.appendChild(closeFooterBtn);