feat(i18n): localize panel shell controls

This commit is contained in:
youzini
2026-06-05 10:50:51 +00:00
parent 68bf1eca42
commit 517a17d974
6 changed files with 104 additions and 14 deletions

View File

@@ -5,6 +5,7 @@
export default {
"common.appName": "ST-BME",
"common.cancel": "Cancel",
"common.clauseSeparator": "; ",
"common.close": "Close",
"common.confirm": "Confirm",
"common.delete": "Delete",
@@ -45,6 +46,11 @@ export default {
"panel.entry.menuLabel": "Memory Graph",
"panel.entry.openPanelAction": "Open Panel",
"panel.entry.openFailed": "Memory Graph panel failed to load. Check the console for details.",
"panel.entry.preloadFailed": "Memory Graph panel preloading failed. Try opening it from the menu again later.",
"panel.appearance.locale.subtitle": "Only changes the BME frontend UI. It does not translate chat content, memory nodes, or prompts.",
"panel.appearance.locale.title": "Interface Language",
"panel.appearance.theme.subtitle": "Choose the panel style that best fits the current story mood and reading habits.",
"panel.appearance.theme.title": "Panel Theme",
"panel.title": "ST-BME Memory Graph",
"panel.header.fabToggleTitle": "Show/Hide FAB",
"panel.header.themePickerTitle": "Switch Theme",
@@ -214,4 +220,12 @@ export default {
"status.initial.recall.detail": "Recall has not run yet",
"status.initial.runtime.detail": "Ready",
"status.initial.vector.detail": "Vector tasks have not run yet",
"llm.providerHelp.auto": "Leave blank to reuse the current chat model. BME can detect OpenAI-compatible endpoints, Anthropic Claude, and Google AI Studio / Gemini; full endpoints are normalized into reusable base URLs.",
"llm.providerHelp.baseUrl": "Base URL: {baseUrl}",
"llm.providerHelp.customProvider": "Provider not recognized; treating it as a custom OpenAI-compatible endpoint",
"llm.providerHelp.knownProvider": "Detected provider: {provider}",
"llm.providerHelp.modelFetchUnsupported": "This provider cannot fetch models automatically yet; enter the model name manually.",
"llm.providerHelp.normalizedUrl": "Normalized URL: {apiUrl}",
"llm.providerHelp.transport": "Transport: {transport}",
};

View File

@@ -5,6 +5,7 @@
export default {
"common.appName": "ST-BME",
"common.cancel": "取消",
"common.clauseSeparator": "",
"common.close": "关闭",
"common.confirm": "确认",
"common.delete": "删除",
@@ -45,6 +46,11 @@ export default {
"panel.entry.menuLabel": "记忆图谱",
"panel.entry.openPanelAction": "打开面板",
"panel.entry.openFailed": "记忆图谱面板加载失败,请查看控制台报错",
"panel.entry.preloadFailed": "记忆图谱面板预加载失败,可稍后重试点击菜单",
"panel.appearance.locale.subtitle": "只切换 BME 前端界面,不翻译聊天内容、记忆节点或提示词。",
"panel.appearance.locale.title": "界面语言",
"panel.appearance.theme.subtitle": "选择最适合当前故事氛围和阅读习惯的面板风格。",
"panel.appearance.theme.title": "面板主题",
"panel.title": "ST-BME 记忆图谱",
"panel.header.fabToggleTitle": "显示/隐藏悬浮球",
"panel.header.themePickerTitle": "切换主题",
@@ -214,4 +220,12 @@ export default {
"status.initial.recall.detail": "尚未执行召回",
"status.initial.runtime.detail": "准备就绪",
"status.initial.vector.detail": "尚未执行向量任务",
"llm.providerHelp.auto": "留空时复用当前聊天模型。支持自动识别 OpenAI 兼容渠道、Anthropic Claude、Google AI Studio / Gemini填写完整 endpoint 时会自动规整为可复用的 base URL。",
"llm.providerHelp.baseUrl": "基础地址:{baseUrl}",
"llm.providerHelp.customProvider": "未识别为特定渠道,将按自定义 OpenAI 兼容接口处理",
"llm.providerHelp.knownProvider": "已识别渠道:{provider}",
"llm.providerHelp.modelFetchUnsupported": "该渠道暂不支持自动拉取模型,请手动填写模型名",
"llm.providerHelp.normalizedUrl": "规范化地址:{apiUrl}",
"llm.providerHelp.transport": "请求通道:{transport}",
};

View File

@@ -132,15 +132,23 @@ function buildDocument({ includeExtensionsMenu = true } = {}) {
return document;
}
function buildRuntime(document) {
function buildRuntime(document, initialSettings = {}) {
let settings = { ...initialSettings };
const calls = { opened: 0, hidden: [], css: [] };
const panelModule = { openPanel: () => { calls.opened += 1; } };
const panelModule = {
openPanel: () => { calls.opened += 1; },
updatePanelLocale: (localeMode) => { calls.updatedLocale = localeMode; },
};
return {
calls,
console,
document,
getPanelModule: () => panelModule,
getSettings: () => ({}),
getSettings: () => settings,
updateSettings: (patch) => {
settings = { ...settings, ...patch };
return settings;
},
$: (selector) => ({
hide: () => calls.hidden.push(selector),
css: (name, value) => calls.css.push([selector, name, value]),
@@ -168,6 +176,26 @@ const { initializePanelBridgeController } = await import("../ui/panel-bridge.js"
assert.ok(runtime.calls.hidden.includes("#extensionsMenu"), "magic-wand entry closes extensions menu");
}
{
const document = buildDocument();
const runtime = buildRuntime(document, { uiLocale: "en-US" });
await initializePanelBridgeController(runtime);
const optionsEntry = document.getElementById("option_st_bme_panel");
const wandEntry = document.getElementById("st_bme_extensions_menu_entry");
const fab = document.getElementById("bme-floating-ball");
assert.match(optionsEntry.innerHTML, /Memory Graph/, "legacy menu entry follows English locale");
assert.match(wandEntry.innerHTML, /Memory Graph/, "magic-wand entry follows English locale");
assert.match(fab.innerHTML, /BME Memory Graph/, "floating bootstrap follows English locale");
runtime.updateSettings({ uiLocale: "zh-CN" });
await initializePanelBridgeController(runtime);
assert.match(optionsEntry.innerHTML, /记忆图谱/, "legacy menu entry refreshes when locale changes");
assert.match(wandEntry.innerHTML, /记忆图谱/, "magic-wand entry refreshes when locale changes");
assert.match(fab.innerHTML, /BME 记忆图谱/, "floating bootstrap refreshes when locale changes");
}
{
const document = buildDocument({ includeExtensionsMenu: false });
const runtime = buildRuntime(document);

View File

@@ -252,6 +252,6 @@ export async function initializePanelBridgeController(runtime) {
"[ST-BME] 操控面板加载失败(核心功能不受影响):",
panelError,
);
globalThis.toastr?.error?.("记忆图谱面板预加载失败,可稍后重试点击菜单", "ST-BME");
globalThis.toastr?.error?.(t("panel.entry.preloadFailed"), "ST-BME");
}
}

View File

@@ -3235,8 +3235,30 @@
<div class="bme-config-card">
<div class="bme-config-card-head">
<div>
<div class="bme-config-card-title">面板主题</div>
<div class="bme-config-card-subtitle">
<div class="bme-config-card-title" data-i18n="panel.appearance.locale.title">界面语言</div>
<div class="bme-config-card-subtitle" data-i18n="panel.appearance.locale.subtitle">
只切换 BME 前端界面,不翻译聊天内容、记忆节点或提示词。
</div>
</div>
</div>
<div class="bme-config-row">
<label for="bme-setting-ui-locale" data-i18n="i18n.locale.setting.label">界面语言</label>
<select id="bme-setting-ui-locale" class="bme-config-select">
<option value="auto" data-i18n="i18n.locale.auto">自动</option>
<option value="zh-CN" data-i18n="i18n.locale.zhCN">简体中文</option>
<option value="en-US" data-i18n="i18n.locale.enUS">English</option>
</select>
<div class="bme-config-help" data-i18n="i18n.locale.setting.help">
只影响 ST-BME 面板、提示和状态文案,不会翻译聊天内容、记忆节点或提示词。
</div>
</div>
</div>
<div class="bme-config-card">
<div class="bme-config-card-head">
<div>
<div class="bme-config-card-title" data-i18n="panel.appearance.theme.title">面板主题</div>
<div class="bme-config-card-subtitle" data-i18n="panel.appearance.theme.subtitle">
选择最适合当前故事氛围和阅读习惯的面板风格。
</div>
</div>

View File

@@ -93,8 +93,7 @@ function _refreshMemoryLlmProviderHelp(urlValue = null) {
).trim();
if (!rawUrl) {
helpEl.textContent =
"留空时复用当前聊天模型。支持自动识别 OpenAI 兼容渠道、Anthropic Claude、Google AI Studio / Gemini填写完整 endpoint 时会自动规整为可复用的 base URL。";
helpEl.textContent = t("llm.providerHelp.auto");
return;
}
@@ -102,24 +101,26 @@ function _refreshMemoryLlmProviderHelp(urlValue = null) {
const parts = [];
if (resolved.isKnownProvider) {
parts.push(`已识别渠道:${resolved.providerLabel || resolved.providerId || "未知渠道"}`);
parts.push(t("llm.providerHelp.knownProvider", {
provider: resolved.providerLabel || resolved.providerId || t("common.unknown"),
}));
} else {
parts.push("未识别为特定渠道,将按自定义 OpenAI 兼容接口处理");
parts.push(t("llm.providerHelp.customProvider"));
}
if (resolved.transportLabel) {
parts.push(`请求通道:${resolved.transportLabel}`);
parts.push(t("llm.providerHelp.transport", { transport: resolved.transportLabel }));
}
if (resolved.apiUrl && resolved.apiUrl !== rawUrl) {
parts.push(`规范化地址:${resolved.apiUrl}`);
parts.push(t("llm.providerHelp.normalizedUrl", { apiUrl: resolved.apiUrl }));
}
if (resolved.supportsModelFetch !== true) {
parts.push("该渠道暂不支持自动拉取模型,请手动填写模型名");
parts.push(t("llm.providerHelp.modelFetchUnsupported"));
}
helpEl.textContent = parts.join("");
helpEl.textContent = parts.join(t("common.clauseSeparator"));
}
function getDefaultPrompts() {
@@ -7895,6 +7896,10 @@ function _refreshConfigTab() {
"bme-setting-recall-card-user-input-display-mode",
settings.recallCardUserInputDisplayMode ?? "beautify_only",
);
_setInputValue(
"bme-setting-ui-locale",
settings.uiLocale || "auto",
);
_setInputValue(
"bme-setting-notice-display-mode",
settings.noticeDisplayMode ?? "normal",
@@ -8227,6 +8232,13 @@ function _bindConfigControls() {
bindCheckbox("bme-setting-debug-logging-enabled", (checked) => {
_patchSettings({ debugLoggingEnabled: checked });
});
const uiLocaleEl = document.getElementById("bme-setting-ui-locale");
if (uiLocaleEl && uiLocaleEl.dataset.bmeBound !== "true") {
uiLocaleEl.addEventListener("change", () => {
_patchSettings({ uiLocale: uiLocaleEl.value || "auto" });
});
uiLocaleEl.dataset.bmeBound = "true";
}
bindCheckbox("bme-setting-graph-native-force-disable", (checked) => {
_patchSettings({ graphNativeForceDisable: checked });
});