完成配置界面和参数的汉化
This commit is contained in:
parent
5f6dc2d89b
commit
fc8eae7426
@ -75,6 +75,235 @@ export const zhCN = {
|
|||||||
authFailed: "身份验证失败。请重新复制包含令牌的 URL 或更新令牌,然后点击连接。",
|
authFailed: "身份验证失败。请重新复制包含令牌的 URL 或更新令牌,然后点击连接。",
|
||||||
insecureContext: "当前页面为 HTTP,浏览器已禁用设备身份。请使用 HTTPS 或在网关主机上访问 localhost。",
|
insecureContext: "当前页面为 HTTP,浏览器已禁用设备身份。请使用 HTTPS 或在网关主机上访问 localhost。",
|
||||||
},
|
},
|
||||||
|
nodes: {
|
||||||
|
approvalsTitle: "节点审批",
|
||||||
|
approvalsSubtitle: "配置对新节点的默认连接策略",
|
||||||
|
nodesTitle: "节点",
|
||||||
|
nodesSubtitle: "管理已连接的计算节点",
|
||||||
|
devicesTitle: "设备",
|
||||||
|
devicesSubtitle: "管理已配对的设备",
|
||||||
|
target: "目标",
|
||||||
|
targetHint: "允许连接的目标节点类型",
|
||||||
|
hostLabel: "主机",
|
||||||
|
gateway: "网关",
|
||||||
|
node: "节点",
|
||||||
|
selectNode: "选择节点...",
|
||||||
|
noApprovalsNodes: "未找到可审批的节点",
|
||||||
|
scope: "范围",
|
||||||
|
defaults: "默认",
|
||||||
|
security: "安全",
|
||||||
|
securityDefaultHint: "默认安全策略",
|
||||||
|
securityAgentHint: "当前设置: {security}",
|
||||||
|
modeLabel: "模式",
|
||||||
|
useDefault: "使用默认值 ({security})",
|
||||||
|
modelLabel: "模型",
|
||||||
|
securityOptions: {
|
||||||
|
deny: "拒绝",
|
||||||
|
allowlist: "白名单",
|
||||||
|
full: "完全访问",
|
||||||
|
},
|
||||||
|
ask: "询问",
|
||||||
|
askDefaultHint: "连接时的询问策略",
|
||||||
|
askOptions: {
|
||||||
|
off: "关闭",
|
||||||
|
onMiss: "仅缺失时",
|
||||||
|
always: "总是",
|
||||||
|
},
|
||||||
|
askFallback: "后备询问",
|
||||||
|
askFallbackHint: "后备询问策略",
|
||||||
|
fallbackLabel: "后备",
|
||||||
|
autoAllowSkills: "自动允许技能",
|
||||||
|
autoAllowSkillsHint: "自动允许已知技能运行",
|
||||||
|
bindingTitle: "绑定",
|
||||||
|
bindingSubtitle: "将代理绑定到特定节点",
|
||||||
|
save: "保存",
|
||||||
|
saving: "保存中...",
|
||||||
|
bindingRawWarn: "高级模式:请小心编辑 JSON",
|
||||||
|
loadConfigToEdit: "加载配置以编辑绑定",
|
||||||
|
loadConfig: "加载配置",
|
||||||
|
loadApprovalsToEdit: "加载审批配置以编辑",
|
||||||
|
loadApprovals: "加载审批配置",
|
||||||
|
defaultBinding: "默认绑定",
|
||||||
|
defaultBindingHint: "未指定绑定的代理将使用此节点",
|
||||||
|
nodeLabel: "节点",
|
||||||
|
anyNode: "任意节点 (自动调度)",
|
||||||
|
noRunNodes: "未发现可作为运行目标的节点",
|
||||||
|
noNodesFound: "未发现已连接的节点",
|
||||||
|
pending: "待批准",
|
||||||
|
paired: "已配对",
|
||||||
|
noPairedDevices: "暂无已配对设备",
|
||||||
|
approve: "批准",
|
||||||
|
reject: "拒绝",
|
||||||
|
tokens: "访问令牌",
|
||||||
|
tokensNone: "无令牌",
|
||||||
|
revoked: "已撤销",
|
||||||
|
active: "活跃",
|
||||||
|
rotate: "轮换",
|
||||||
|
revoke: "撤销",
|
||||||
|
},
|
||||||
|
debug: {
|
||||||
|
snapshotsTitle: "快照",
|
||||||
|
snapshotsSubtitle: "当前系统状态快照",
|
||||||
|
status: "状态",
|
||||||
|
health: "健康状况",
|
||||||
|
lastHeartbeat: "最近心跳",
|
||||||
|
securityAudit: "安全审计: {label}",
|
||||||
|
criticalIssues: "{count} 个严重问题",
|
||||||
|
warningIssues: "{count} 个警告",
|
||||||
|
infoIssues: "{count} 个提示",
|
||||||
|
noCriticalIssues: "无严重问题",
|
||||||
|
refreshing: "刷新中...",
|
||||||
|
manualRpcTitle: "手动 RPC",
|
||||||
|
manualRpcSubtitle: "手动调用远程过程",
|
||||||
|
methodLabel: "方法",
|
||||||
|
paramsLabel: "参数 (JSON)",
|
||||||
|
call: "调用",
|
||||||
|
modelsTitle: "模型",
|
||||||
|
modelsSubtitle: "已加载的模型",
|
||||||
|
eventLogTitle: "事件日志",
|
||||||
|
eventLogSubtitle: "最近的系统事件",
|
||||||
|
noEvents: "暂无事件。",
|
||||||
|
},
|
||||||
|
logs: {
|
||||||
|
logsTitle: "日志",
|
||||||
|
logsSubtitle: "实时系统日志流",
|
||||||
|
export: "导出",
|
||||||
|
exportFiltered: "导出筛选结果",
|
||||||
|
exportVisible: "导出可见日志",
|
||||||
|
noEntries: "暂无日志条目。",
|
||||||
|
filterLabel: "过滤",
|
||||||
|
searchPlaceholder: "搜索日志...",
|
||||||
|
autoFollow: "自动滚动",
|
||||||
|
fileLabel: "日志文件",
|
||||||
|
truncatedWarn: "日志已截断,仅显示最近的部分。",
|
||||||
|
trace: "Trace",
|
||||||
|
debug: "Debug",
|
||||||
|
info: "Info",
|
||||||
|
warn: "Warn",
|
||||||
|
error: "Error",
|
||||||
|
fatal: "Fatal",
|
||||||
|
},
|
||||||
|
config: {
|
||||||
|
subtitle: "全局配置编辑器",
|
||||||
|
searchPlaceholder: "搜索配置项...",
|
||||||
|
allSettings: "所有设置",
|
||||||
|
sections: {
|
||||||
|
env: "环境",
|
||||||
|
update: "更新",
|
||||||
|
agents: "代理",
|
||||||
|
auth: "认证",
|
||||||
|
channels: "渠道",
|
||||||
|
messages: "消息",
|
||||||
|
commands: "命令",
|
||||||
|
hooks: "钩子",
|
||||||
|
skills: "技能",
|
||||||
|
tools: "工具",
|
||||||
|
gateway: "网关",
|
||||||
|
wizard: "向导",
|
||||||
|
meta: "元数据",
|
||||||
|
diagnostics: "诊断",
|
||||||
|
logging: "日志",
|
||||||
|
browser: "浏览器",
|
||||||
|
ui: "界面",
|
||||||
|
models: "模型",
|
||||||
|
nodeHost: "节点主机",
|
||||||
|
bindings: "绑定",
|
||||||
|
broadcast: "广播",
|
||||||
|
audio: "音频",
|
||||||
|
media: "媒体",
|
||||||
|
approvals: "审批",
|
||||||
|
session: "会话",
|
||||||
|
cron: "定时任务",
|
||||||
|
web: "Web 服务",
|
||||||
|
discovery: "发现",
|
||||||
|
canvasHost: "画布主机",
|
||||||
|
talk: "语音",
|
||||||
|
plugins: "插件",
|
||||||
|
},
|
||||||
|
formMode: "表单模式",
|
||||||
|
rawMode: "原始模式",
|
||||||
|
reload: "重新加载",
|
||||||
|
save: "保存",
|
||||||
|
apply: "应用",
|
||||||
|
update: "更新",
|
||||||
|
noChanges: "没有更改",
|
||||||
|
schema: {
|
||||||
|
logging: {
|
||||||
|
label: "日志",
|
||||||
|
description: "日志级别和输出配置",
|
||||||
|
consoleLevel: { label: "控制台级别", description: "控制台日志的输出级别" },
|
||||||
|
consoleStyle: { label: "控制台样式", description: "控制台日志的格式 (plain/json)" },
|
||||||
|
file: { label: "文件日志", description: "日志文件路径" },
|
||||||
|
level: { label: "日志级别", description: "全局日志级别 (trace/debug/info/warn/error)" },
|
||||||
|
redactPatterns: { label: "脱敏模式", description: "用于在日志中隐藏敏感信息的正则表达式" },
|
||||||
|
redactSensitive: { label: "自动脱敏", description: "自动隐藏已知的敏感键值 (如 password, token)" },
|
||||||
|
},
|
||||||
|
diagnostics: {
|
||||||
|
label: "诊断",
|
||||||
|
description: "OpenTelemetry 与调试选项",
|
||||||
|
enabled: { label: "启用诊断", description: "开启系统诊断功能" },
|
||||||
|
flags: { label: "诊断标志", description: "启用特定模块的详细日志 (如 *telegram*)" },
|
||||||
|
cacheTrace: {
|
||||||
|
label: "缓存跟踪",
|
||||||
|
description: "配置缓存操作的跟踪详细程度",
|
||||||
|
enabled: { label: "启用缓存跟踪", description: "记录嵌入式代理运行的缓存跟踪快照" },
|
||||||
|
filePath: { label: "缓存跟踪文件路径", description: "缓存跟踪日志的 JSONL 输出路径" },
|
||||||
|
includeMessages: { label: "包含消息", description: "在跟踪输出中包含完整的消息负载" },
|
||||||
|
includePrompt: { label: "包含提示词", description: "在跟踪中包含提示词文本" },
|
||||||
|
includeSystem: { label: "包含系统提示", description: "在跟踪中包含系统提示词" },
|
||||||
|
},
|
||||||
|
otel: {
|
||||||
|
label: "OpenTelemetry",
|
||||||
|
description: "OTLP 导出配置",
|
||||||
|
enabled: { label: "启用 OpenTelemetry", description: "发送遥测数据到 OTLP 端点" },
|
||||||
|
endpoint: { label: "OTLP 端点", description: "OTLP 收集器 URL" },
|
||||||
|
flushInterval: { label: "刷新间隔 (ms)", description: "数据上报的频率" },
|
||||||
|
headers: { label: "请求头", description: "附加的 HTTP 请求头" },
|
||||||
|
logsEnabled: { label: "启用日志", description: "发送日志数据" },
|
||||||
|
metricsEnabled: { label: "启用指标", description: "发送指标数据" },
|
||||||
|
tracesEnabled: { label: "启用链路追踪", description: "发送链路追踪数据" },
|
||||||
|
protocol: { label: "协议", description: "传输协议 (http/grpc)" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
label: "更新",
|
||||||
|
description: "配置更新行为",
|
||||||
|
channel: { label: "更新渠道", description: "选择更新发布渠道 (main/beta/dev)" },
|
||||||
|
},
|
||||||
|
meta: {
|
||||||
|
label: "元数据",
|
||||||
|
description: "系统运行信息",
|
||||||
|
lastRunAt: { label: "上次运行时间", description: "" },
|
||||||
|
lastRunCommand: { label: "上次运行命令", description: "" },
|
||||||
|
lastRunCommit: { label: "上次运行提交", description: "" },
|
||||||
|
lastRunMode: { label: "上次运行模式", description: "" },
|
||||||
|
lastRunVersion: { label: "上次运行版本", description: "" },
|
||||||
|
},
|
||||||
|
auth: {
|
||||||
|
label: "认证",
|
||||||
|
description: "身份验证设置",
|
||||||
|
allowTailscale: { label: "允许 Tailscale", description: "允许 Tailscale 访问" },
|
||||||
|
mode: { label: "模式", description: "认证模式 (token/password)" },
|
||||||
|
password: { label: "网关密码", description: "Tailscale funnel 需要此密码" },
|
||||||
|
token: { label: "网关令牌", description: "访问网关所需的令牌" },
|
||||||
|
gatewayPassword: { label: "网关密码", description: "Tailscale funnel 需要此密码" },
|
||||||
|
gatewayToken: { label: "网关令牌", description: "访问网关所需的令牌" },
|
||||||
|
},
|
||||||
|
nodeHost: {
|
||||||
|
label: "节点主机",
|
||||||
|
description: "网关与节点通信配置",
|
||||||
|
allowTobacco: { label: "允许烟草内容", description: "是否允许涉及烟草的内容" },
|
||||||
|
password: { label: "网关密码", description: "连接网关所需的密码" },
|
||||||
|
token: { label: "网关令牌", description: "连接网关所需的令牌" },
|
||||||
|
mode: { label: "认证模式", description: "选择 token 或 password 认证" },
|
||||||
|
},
|
||||||
|
env: {
|
||||||
|
label: "环境变量",
|
||||||
|
description: "传递给网关进程的环境变量",
|
||||||
|
allSubsections: { label: "所有变量", description: "所有配置的环境变量列表" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
channels: {
|
channels: {
|
||||||
title: "渠道",
|
title: "渠道",
|
||||||
subtitle: "管理消息渠道连接",
|
subtitle: "管理消息渠道连接",
|
||||||
@ -309,6 +538,18 @@ export const zhCN = {
|
|||||||
reasonAllowlist: "被白名单拦截",
|
reasonAllowlist: "被白名单拦截",
|
||||||
enable: "启用",
|
enable: "启用",
|
||||||
disable: "禁用",
|
disable: "禁用",
|
||||||
|
names: {
|
||||||
|
"1password": "1Password CLI",
|
||||||
|
"apple-notes": "备忘录 (Apple Notes)",
|
||||||
|
"apple-reminders": "提醒事项 (Apple Reminders)",
|
||||||
|
"github": "GitHub",
|
||||||
|
"openai-whisper": "OpenAI Whisper",
|
||||||
|
},
|
||||||
|
descriptions: {
|
||||||
|
"1password": "设置并使用 1Password CLI (op)。用于安装 CLI、启用桌面集成、登录账户或通过 op 访问机密。",
|
||||||
|
"apple-notes": "在 macOS 上通过 `memo` CLI 管理 Apple Notes (创建、查看、编辑、删除、搜索、移动和导出笔记)。",
|
||||||
|
"apple-reminders": "在 macOS 上通过 `remindctl` CLI 管理 Apple Reminders (列表、添加、编辑、完成、删除)。",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
instances: {
|
instances: {
|
||||||
title: "已连接实例",
|
title: "已连接实例",
|
||||||
|
|||||||
@ -27,6 +27,34 @@ function jsonValue(value: unknown): string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resolveNodeLabels(
|
||||||
|
path: Array<string | number>,
|
||||||
|
schema: JsonSchema,
|
||||||
|
hints: ConfigUiHints
|
||||||
|
): { label: string; help?: string } {
|
||||||
|
const hint = hintForPath(path, hints);
|
||||||
|
// Try translation
|
||||||
|
// Filter out numeric indices to handle array items if necessary,
|
||||||
|
// but for settings keys (which are object properties), we usually want the specific path.
|
||||||
|
// However, many array items might share the same schema.
|
||||||
|
// For now, we focus on the settings panel which is mostly object paths.
|
||||||
|
const strPath = path.join(".");
|
||||||
|
const schemaPath = `config.schema.${strPath}`;
|
||||||
|
const labelKey = `${schemaPath}.label`;
|
||||||
|
const descKey = `${schemaPath}.description`;
|
||||||
|
|
||||||
|
const translatedLabel = t(labelKey);
|
||||||
|
const translatedDesc = t(descKey);
|
||||||
|
|
||||||
|
const fallbackLabel = hint?.label ?? schema.title ?? humanize(String(path.at(-1)));
|
||||||
|
const fallbackHelp = hint?.help ?? schema.description;
|
||||||
|
|
||||||
|
return {
|
||||||
|
label: translatedLabel && translatedLabel !== labelKey ? translatedLabel : fallbackLabel,
|
||||||
|
help: translatedDesc && translatedDesc !== descKey ? translatedDesc : fallbackHelp,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// SVG Icons as template literals
|
// SVG Icons as template literals
|
||||||
const icons = {
|
const icons = {
|
||||||
chevronDown: html`<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg>`,
|
chevronDown: html`<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg>`,
|
||||||
@ -49,9 +77,12 @@ export function renderNode(params: {
|
|||||||
const { schema, value, path, hints, unsupported, disabled, onPatch } = params;
|
const { schema, value, path, hints, unsupported, disabled, onPatch } = params;
|
||||||
const showLabel = params.showLabel ?? true;
|
const showLabel = params.showLabel ?? true;
|
||||||
const type = schemaType(schema);
|
const type = schemaType(schema);
|
||||||
const hint = hintForPath(path, hints);
|
const hint = hintForPath(path, hints); // Keep for sensitive check below?
|
||||||
const label = hint?.label ?? schema.title ?? humanize(String(path.at(-1)));
|
// Actually resolveNodeLabels does `hintForPath` internally but we might need `hint` variable for other things like `sensitive`.
|
||||||
const help = hint?.help ?? schema.description;
|
// `renderNode` doesn't use `hint` for anything else except label/help.
|
||||||
|
// `pathKey` is used below.
|
||||||
|
|
||||||
|
const { label, help } = resolveNodeLabels(path, schema, hints);
|
||||||
const key = pathKey(path);
|
const key = pathKey(path);
|
||||||
|
|
||||||
if (unsupported.has(key)) {
|
if (unsupported.has(key)) {
|
||||||
@ -229,8 +260,7 @@ function renderTextInput(params: {
|
|||||||
const { schema, value, path, hints, disabled, onPatch, inputType } = params;
|
const { schema, value, path, hints, disabled, onPatch, inputType } = params;
|
||||||
const showLabel = params.showLabel ?? true;
|
const showLabel = params.showLabel ?? true;
|
||||||
const hint = hintForPath(path, hints);
|
const hint = hintForPath(path, hints);
|
||||||
const label = hint?.label ?? schema.title ?? humanize(String(path.at(-1)));
|
const { label, help } = resolveNodeLabels(path, schema, hints);
|
||||||
const help = hint?.help ?? schema.description;
|
|
||||||
const isSensitive = hint?.sensitive ?? isSensitivePath(path);
|
const isSensitive = hint?.sensitive ?? isSensitivePath(path);
|
||||||
const placeholder =
|
const placeholder =
|
||||||
hint?.placeholder ??
|
hint?.placeholder ??
|
||||||
@ -297,8 +327,7 @@ function renderNumberInput(params: {
|
|||||||
const { schema, value, path, hints, disabled, onPatch } = params;
|
const { schema, value, path, hints, disabled, onPatch } = params;
|
||||||
const showLabel = params.showLabel ?? true;
|
const showLabel = params.showLabel ?? true;
|
||||||
const hint = hintForPath(path, hints);
|
const hint = hintForPath(path, hints);
|
||||||
const label = hint?.label ?? schema.title ?? humanize(String(path.at(-1)));
|
const { label, help } = resolveNodeLabels(path, schema, hints);
|
||||||
const help = hint?.help ?? schema.description;
|
|
||||||
const displayValue = value ?? schema.default ?? "";
|
const displayValue = value ?? schema.default ?? "";
|
||||||
const numValue = typeof displayValue === "number" ? displayValue : 0;
|
const numValue = typeof displayValue === "number" ? displayValue : 0;
|
||||||
|
|
||||||
@ -348,8 +377,7 @@ function renderSelect(params: {
|
|||||||
const { schema, value, path, hints, disabled, options, onPatch } = params;
|
const { schema, value, path, hints, disabled, options, onPatch } = params;
|
||||||
const showLabel = params.showLabel ?? true;
|
const showLabel = params.showLabel ?? true;
|
||||||
const hint = hintForPath(path, hints);
|
const hint = hintForPath(path, hints);
|
||||||
const label = hint?.label ?? schema.title ?? humanize(String(path.at(-1)));
|
const { label, help } = resolveNodeLabels(path, schema, hints);
|
||||||
const help = hint?.help ?? schema.description;
|
|
||||||
const resolvedValue = value ?? schema.default;
|
const resolvedValue = value ?? schema.default;
|
||||||
const currentIndex = options.findIndex(
|
const currentIndex = options.findIndex(
|
||||||
(opt) => opt === resolvedValue || String(opt) === String(resolvedValue),
|
(opt) => opt === resolvedValue || String(opt) === String(resolvedValue),
|
||||||
@ -391,8 +419,7 @@ function renderObject(params: {
|
|||||||
const { schema, value, path, hints, unsupported, disabled, onPatch } = params;
|
const { schema, value, path, hints, unsupported, disabled, onPatch } = params;
|
||||||
const showLabel = params.showLabel ?? true;
|
const showLabel = params.showLabel ?? true;
|
||||||
const hint = hintForPath(path, hints);
|
const hint = hintForPath(path, hints);
|
||||||
const label = hint?.label ?? schema.title ?? humanize(String(path.at(-1)));
|
const { label, help } = resolveNodeLabels(path, schema, hints);
|
||||||
const help = hint?.help ?? schema.description;
|
|
||||||
|
|
||||||
const fallback = value ?? schema.default;
|
const fallback = value ?? schema.default;
|
||||||
const obj = fallback && typeof fallback === "object" && !Array.isArray(fallback)
|
const obj = fallback && typeof fallback === "object" && !Array.isArray(fallback)
|
||||||
@ -490,8 +517,7 @@ function renderArray(params: {
|
|||||||
const { schema, value, path, hints, unsupported, disabled, onPatch } = params;
|
const { schema, value, path, hints, unsupported, disabled, onPatch } = params;
|
||||||
const showLabel = params.showLabel ?? true;
|
const showLabel = params.showLabel ?? true;
|
||||||
const hint = hintForPath(path, hints);
|
const hint = hintForPath(path, hints);
|
||||||
const label = hint?.label ?? schema.title ?? humanize(String(path.at(-1)));
|
const { label, help } = resolveNodeLabels(path, schema, hints);
|
||||||
const help = hint?.help ?? schema.description;
|
|
||||||
|
|
||||||
const itemsSchema = Array.isArray(schema.items) ? schema.items[0] : schema.items;
|
const itemsSchema = Array.isArray(schema.items) ? schema.items[0] : schema.items;
|
||||||
if (!itemsSchema) {
|
if (!itemsSchema) {
|
||||||
|
|||||||
@ -185,8 +185,16 @@ export function renderConfigForm(props: ConfigFormProps) {
|
|||||||
? (() => {
|
? (() => {
|
||||||
const { sectionKey, subsectionKey, schema: node } = subsectionContext;
|
const { sectionKey, subsectionKey, schema: node } = subsectionContext;
|
||||||
const hint = hintForPath([sectionKey, subsectionKey], props.uiHints);
|
const hint = hintForPath([sectionKey, subsectionKey], props.uiHints);
|
||||||
const label = hint?.label ?? node.title ?? humanize(subsectionKey);
|
|
||||||
const description = hint?.help ?? node.description ?? "";
|
// Try translation first
|
||||||
|
const schemaPath = `config.schema.${sectionKey}.${subsectionKey}`;
|
||||||
|
const labelKey = `${schemaPath}.label`;
|
||||||
|
const descKey = `${schemaPath}.description`;
|
||||||
|
const translatedLabel = t(labelKey);
|
||||||
|
const translatedDesc = t(descKey);
|
||||||
|
|
||||||
|
const label = translatedLabel !== labelKey ? translatedLabel : (hint?.label ?? node.title ?? humanize(subsectionKey));
|
||||||
|
const description = translatedDesc !== descKey ? translatedDesc : (hint?.help ?? node.description ?? "");
|
||||||
const sectionValue = (value as Record<string, unknown>)[sectionKey];
|
const sectionValue = (value as Record<string, unknown>)[sectionKey];
|
||||||
const scopedValue =
|
const scopedValue =
|
||||||
sectionValue && typeof sectionValue === "object"
|
sectionValue && typeof sectionValue === "object"
|
||||||
@ -221,14 +229,23 @@ export function renderConfigForm(props: ConfigFormProps) {
|
|||||||
})()
|
})()
|
||||||
: filteredEntries.map(([key, node]) => {
|
: filteredEntries.map(([key, node]) => {
|
||||||
const meta = getSectionMeta(key);
|
const meta = getSectionMeta(key);
|
||||||
|
// Try translation for top-level schema items if not in meta
|
||||||
|
const schemaPath = `config.schema.${key}`;
|
||||||
|
const labelKey = `${schemaPath}.label`;
|
||||||
|
const descKey = `${schemaPath}.description`;
|
||||||
|
const translatedLabel = t(labelKey);
|
||||||
|
const translatedDesc = t(descKey);
|
||||||
|
|
||||||
|
const label = translatedLabel !== labelKey ? translatedLabel : meta.label;
|
||||||
|
const description = translatedDesc !== descKey ? translatedDesc : meta.description;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<section class="config-section-card" id="config-section-${key}">
|
<section class="config-section-card" id="config-section-${key}">
|
||||||
<div class="config-section-card__header">
|
<div class="config-section-card__header">
|
||||||
<span class="config-section-card__icon">${getSectionIcon(key)}</span>
|
<span class="config-section-card__icon">${getSectionIcon(key)}</span>
|
||||||
<div class="config-section-card__titles">
|
<div class="config-section-card__titles">
|
||||||
<h3 class="config-section-card__title">${meta.label}</h3>
|
<h3 class="config-section-card__title">${label}</h3>
|
||||||
${meta.description
|
${description
|
||||||
? html`<p class="config-section-card__desc">${meta.description}</p>`
|
? html`<p class="config-section-card__desc">${meta.description}</p>`
|
||||||
: nothing}
|
: nothing}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -88,6 +88,25 @@ const SECTIONS: Array<{ key: string; label: string }> = [
|
|||||||
{ key: "tools", label: t("config.sections.tools") },
|
{ key: "tools", label: t("config.sections.tools") },
|
||||||
{ key: "gateway", label: t("config.sections.gateway") },
|
{ key: "gateway", label: t("config.sections.gateway") },
|
||||||
{ key: "wizard", label: t("config.sections.wizard") },
|
{ key: "wizard", label: t("config.sections.wizard") },
|
||||||
|
{ key: "meta", label: t("config.sections.meta") },
|
||||||
|
{ key: "diagnostics", label: t("config.sections.diagnostics") },
|
||||||
|
{ key: "logging", label: t("config.sections.logging") },
|
||||||
|
{ key: "browser", label: t("config.sections.browser") },
|
||||||
|
{ key: "ui", label: t("config.sections.ui") },
|
||||||
|
{ key: "models", label: t("config.sections.models") },
|
||||||
|
{ key: "nodeHost", label: t("config.sections.nodeHost") },
|
||||||
|
{ key: "bindings", label: t("config.sections.bindings") },
|
||||||
|
{ key: "broadcast", label: t("config.sections.broadcast") },
|
||||||
|
{ key: "audio", label: t("config.sections.audio") },
|
||||||
|
{ key: "media", label: t("config.sections.media") },
|
||||||
|
{ key: "approvals", label: t("config.sections.approvals") },
|
||||||
|
{ key: "session", label: t("config.sections.session") },
|
||||||
|
{ key: "cron", label: t("config.sections.cron") },
|
||||||
|
{ key: "web", label: t("config.sections.web") },
|
||||||
|
{ key: "discovery", label: t("config.sections.discovery") },
|
||||||
|
{ key: "canvasHost", label: t("config.sections.canvasHost") },
|
||||||
|
{ key: "talk", label: t("config.sections.talk") },
|
||||||
|
{ key: "plugins", label: t("config.sections.plugins") },
|
||||||
];
|
];
|
||||||
|
|
||||||
type SubsectionEntry = {
|
type SubsectionEntry = {
|
||||||
|
|||||||
@ -92,9 +92,9 @@ function renderSkill(skill: SkillStatusEntry, props: SkillsProps) {
|
|||||||
<div class="list-item">
|
<div class="list-item">
|
||||||
<div class="list-main">
|
<div class="list-main">
|
||||||
<div class="list-title">
|
<div class="list-title">
|
||||||
${skill.emoji ? `${skill.emoji} ` : ""}${skill.name}
|
${skill.emoji ? `${skill.emoji} ` : ""}${t(`skills.names.${skill.skillKey}`) !== `skills.names.${skill.skillKey}` ? t(`skills.names.${skill.skillKey}`) : skill.name}
|
||||||
</div>
|
</div>
|
||||||
<div class="list-sub">${clampText(skill.description, 140)}</div>
|
<div class="list-sub">${clampText((t(`skills.descriptions.${skill.skillKey}`) !== `skills.descriptions.${skill.skillKey}` ? t(`skills.descriptions.${skill.skillKey}`) : skill.description), 140)}</div>
|
||||||
<div class="chip-row" style="margin-top: 6px;">
|
<div class="chip-row" style="margin-top: 6px;">
|
||||||
<span class="chip">${skill.source}</span>
|
<span class="chip">${skill.source}</span>
|
||||||
<span class="chip ${skill.eligible ? "chip-ok" : "chip-warn"}">
|
<span class="chip ${skill.eligible ? "chip-ok" : "chip-warn"}">
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user