Ctrl K
ComunidadGuías…

scripts/gen_i18n_dict.py

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
#!/usr/bin/env python3
"""生成管理页 i18n 字典追加块并插入 scout/web/static/i18n.js"""
import json
import re, io, sys

# (zh, en) 翻译映射 —— 覆盖 11 个管理页静态文本 + JS 纯字符串
TRANS = [
    ("'+ 添加'", None),  # 占位符,实际由下面覆盖
]

# 主翻译表
M = {
    "0 核心": "0 cores",
    "CPU 使用率": "CPU Usage",
    "CPU 使用率 (%)": "CPU Usage (%)",
    "AI 插件生成器 - Scout": "AI Plugin Builder - Scout",
    "AI 正在生成插件代码,请稍候...": "AI is generating plugin code, please wait...",
    "AI 生成的插件": "AI-generated plugin",
    "EventBus · 模块间松耦合通信": "EventBus · loose-coupling inter-module communication",
    "IM 渠道目标": "IM channel target",
    "JSON 格式错误": "Invalid JSON format",
    "SKILL.md 内容已复制": "SKILL.md content copied",
    "Webhook 管理 · Scout Agent": "Webhook Management · Scout Agent",
    "auto — 只读放行,写操作需白名单(推荐)": "auto — read-only allowed, writes require whitelist (recommended)",
    "created 新增": "created",
    "deleted 删除": "deleted",
    "event 触发器": "event trigger",
    "modified 修改": "modified",
    "never — 全部放行(危险命令仍硬拦截)": "never — allow all (dangerous commands still blocked)",
    "prompt — 白名单外拒绝+通知(保守)": "prompt — reject non-whitelisted + notify (conservative)",
    "shell 命令正则白名单(逗号分隔)": "shell command regex whitelist (comma-separated)",
    "writes — 文件写放行,高危需白名单": "writes — file writes allowed, high-risk needs whitelist",
    "← 返回首页": "← Back to Home",
    "← 选择一个会话查看执行时间线": "← Select a session to view the execution timeline",
    "⏳ 安装中...": "⏳ Installing...",
    "⏳ 搜索中...": "⏳ Searching...",
    "⏳ 生成中...": "⏳ Generating...",
    "⚠ 图片不会被识读": "⚠ Images will not be read",
    "⚡ 触发方式": "⚡ Trigger type",
    "✅ 已安装": "✅ Installed",
    "✓ 已保存": "✓ Saved",
    "✓ 配置格式正确": "✓ Config format is valid",
    "✨ 生成插件代码": "✨ Generate plugin code",
    "❌ 停止": "❌ Stopped",
    "➕ 创建插件": "➕ Create Plugin",
    "下午好!希望您今天过得愉快!": "Good afternoon! Hope you have a nice day!",
    "个可复用方案": "reusable solutions",
    "主模型不收图(交视觉模型识图)": "Main model won't take images (use a vision model)",
    "事件处理失败的记录": "Records of failed event processing",
    "事件总线 · Scout Agent": "Event Bus · Scout Agent",
    "事件流": "Event stream",
    "事件类型:": "Event type:",
    "事件触发器": "Event trigger",
    "事件触发(订阅 EventBus 事件)": "Event trigger (subscribe to EventBus events)",
    "今日": "Today",
    "从网上搜到的 Skill(SKILL.md)安装到这里,对话时自动触发": "Skills found online (SKILL.md) install here and trigger automatically in conversation",
    "仓库根地址": "Repository root URL",
    "任务完成": "Task completed",
    "任务异常": "Task error",
    "作者:": "Author:",
    "你好": "Hello",
    "使用 SSL/TLS": "Use SSL/TLS",
    "保存偏好": "Save preferences",
    "保存后插件会自动重新加载": "Plugins auto-reload after saving",
    "保存失败:": "Save failed:",
    "保存策略": "Save policy",
    "保存配置失败": "Failed to save config",
    "保存配置失败:": "Failed to save config:",
    "先输入功能描述,再搜索现成方案": "Enter a feature description first, then search existing solutions",
    "全年": "Full year",
    "全部事件": "All events",
    "关键词-响应映射配置": "Keyword-response mapping config",
    "关键词插件": "Keyword plugin",
    "关闭": "Close",
    "关闭后所有跨渠道推送暂停": "Cross-channel push pauses when off",
    "关闭视觉处理": "Disable vision",
    "其他": "Other",
    "内存使用率": "Memory Usage",
    "内存使用率 (%)": "Memory Usage (%)",
    "切换插件状态失败:": "Failed to toggle plugin state:",
    "创建一个": "Create a",
    "前往": "Go to",
    "加载技能失败:": "Failed to load skills:",
    "加载插件失败": "Failed to load plugins",
    "加载插件失败:": "Failed to load plugins:",
    "加载配置失败": "Failed to load config",
    "加载配置失败:": "Failed to load config:",
    "危险操作时推送通知": "Push notification on dangerous operations",
    "卸载失败": "Uninstall failed",
    "卸载失败:": "Uninstall failed:",
    "去设置": "Open settings",
    "去重窗口 (秒)": "Deduplication window (sec)",
    "反思": "Reflection",
    "发送测试通知": "Send test notification",
    "可选,用于重要通知的邮件备份": "Optional, email backup for important notifications",
    "启动监听": "Start listening",
    "启用通知推送": "Enable notification push",
    "图片将直接发给当前模型": "Images go straight to the current model",
    "图片已转成文字描述": "Images were converted to text descriptions",
    "在任务模板中使用占位符引用文件信息,例如:": "Use placeholders in task templates to reference file info, e.g.:",
    "在左侧添加要监听的目录(支持递归)。": "Add directories to watch on the left (recursive supported).",
    "基础模板": "Basic template",
    "填成与主模型一样的名字 = 声明「主模型自己能看图」,图片将直接发给它(自定义模型最常用)。": "Enter the same name as the main model to declare native vision — images go straight to it (most common choice for custom models).",
    "安装失败 (HTTP": "Install failed (HTTP",
    "安装失败:": "Install failed:",
    "安装成功!": "Install succeeded!",
    "安装超时(仓库可能过大或网络慢)": "Install timeout (repo may be too large or network slow)",
    "定时触发": "Scheduled trigger",
    "实时监控 Scout Agent 运行状态": "Real-time monitoring of Scout Agent status",
    "实测不收图片": "Tested: rejects images",
    "实测能否看图": "Test vision",
    "实测能看图": "Tested: can see images",
    "审批策略 approval_policy": "Approval policy (approval_policy)",
    "工具": "Tools",
    "工具白名单(逗号分隔,空=不限)": "Tool whitelist (comma-separated, empty=unlimited)",
    "工具类型: 0": "Tool types: 0",
    "工具调用": "Tool calls",
    "工具黑名单": "Tool blacklist",
    "已加载": "Loaded",
    "已卸载": "Unloaded",
    "已取消:插件创建需要密码确认": "Cancelled: plugin creation requires password confirmation",
    "已复制:": "Copied:",
    "已实测:不收图片": "Tested: rejects images",
    "已实测:能看图": "Tested: can see images",
    "已禁用": "Disabled",
    "已选中,请手动复制 (Ctrl+C)": "Selected, please copy manually (Ctrl+C)",
    "帮助": "Help",
    "常用字段": "Common fields",
    "平均延迟": "Avg latency",
    "建议使用验证功能检查配置": "Use the validate feature to check config",
    "当前模型不直接看图,将由": "This model can't see images directly —",
    "当前配置无可用视觉模型": "no vision model is available in the current config",
    "总 Token": "Total Tokens",
    "总 Token 消耗": "Total token usage",
    "总会话数: 0": "Total sessions: 0",
    "总插件数": "Total plugins",
    "总调用次数": "Total calls",
    "您好!有什么可以帮助您的吗?": "Hello! How can I help you?",
    "我可以帮您解答问题、完成任务等。": "I can answer questions, complete tasks, etc.",
    "手动触发": "Manual trigger",
    "技能详情": "Skill details",
    "按模型统计": "By model",
    "按类型开关": "Toggle by type",
    "捕获的文件变化(可配合触发器实现自动处理)": "Captured file changes (can auto-process with triggers)",
    "探测中…": "Testing…",
    "探测失败": "Vision test failed",
    "探测请求失败": "Vision test request failed",
    "控制哪些类型的通知值得推送": "Control which notification types are worth pushing",
    "推送历史": "Push history",
    "推送规则": "Push rules",
    "描述你想要的插件功能,AI 会自动生成完整的插件代码": "Describe the plugin feature you want; AI will generate complete plugin code",
    "提醒": "Reminder",
    "插件不存在": "Plugin not found",
    "插件代码不能为空": "Plugin code cannot be empty",
    "插件代码生成成功!": "Plugin code generated!",
    "插件保存成功!": "Plugin saved!",
    "插件名称只能包含小写字母、数字和下划线,且必须以字母开头": "Plugin name must contain only lowercase letters, digits, underscores and start with a letter",
    "插件管理 - Scout Agent": "Plugin Management - Scout Agent",
    "插件配置": "Plugin config",
    "插件配置编辑": "Plugin config editor",
    "插件配置编辑 - Scout Agent": "Plugin Config Editor - Scout Agent",
    "搜索失败:": "Search failed:",
    "操作失败": "Operation failed",
    "敏感词1": "Sensitive word 1",
    "敏感词2": "Sensitive word 2",
    "敏感词过滤配置": "Sensitive word filter config",
    "文件监听 · Scout Agent": "File Watcher · Scout Agent",
    "新建 Webhook": "New Webhook",
    "新建定时任务": "New scheduled task",
    "新建触发器": "New trigger",
    "无人值守策略": "Unattended policy",
    "无描述": "No description",
    "无法判定": "Cannot determine",
    "早上好!新的一天开始了!": "Good morning! A new day begins!",
    "时间段问候配置": "Time-of-day greeting config",
    "晚上好!注意休息哦!": "Good evening! Rest well!",
    "暂无事件": "No events",
    "更新 Agent 状态失败:": "Failed to update agent status:",
    "更新插件状态失败:": "Failed to update plugin state:",
    "更新系统状态失败:": "Failed to update system status:",
    "最低级别": "Minimum level",
    "最多保留 100 条": "Keep at most 100 entries",
    "最多保留 500 条": "Keep at most 500 entries",
    "最近 30 天": "Last 30 days",
    "最近会话(按 trace 聚合)": "Recent sessions (aggregated by trace)",
    "最近调用记录": "Recent call records",
    "未加载": "Not loaded",
    "未指定插件名称": "Plugin name not specified",
    "未知": "Unknown",
    "未能证实(疑似网关丢弃图片)": "Unverified (the gateway may drop images silently)",
    "未送达": "Not delivered",
    "本周": "This week",
    "格式化": "Format",
    "格式化成功": "Formatted",
    "模型监控": "Model Monitor",
    "模型监控 · Scout Agent": "Model Monitor · Scout Agent",
    "每日 Token 消耗趋势": "Daily token usage trend",
    "注意事项": "Notes",
    "活跃会话": "Active sessions",
    "点击「启动监听」,agent 会持续感知该目录的文件新增/修改/删除。": "Click \"Start listening\"; the agent keeps sensing file add/modify/delete in this directory.",
    "版本:": "Version:",
    "状态:": "Status:",
    "生成失败": "Generation failed",
    "生成失败:": "Generation failed:",
    "登录已过期,请重新登录": "Login expired, please log in again",
    "监听目录": "Watch directories",
    "目标": "Target",
    "确定要重新加载所有插件吗?": "Reload all plugins?",
    "确定要重置配置吗?这将恢复到上次保存的状态。": "Reset config? This restores the last saved state.",
    "磁盘使用率": "Disk Usage",
    "空配置,适合自定义": "Empty config, good for customization",
    "端点/网络异常,无法判定": "Endpoint/network error — cannot determine",
    "管理插件 →": "Manage plugins →",
    "系统": "System",
    "系统监控 - Scout Agent": "System Monitor - Scout Agent",
    "级联触发(上游完成后执行)": "Cascade trigger (runs after upstream completes)",
    "缓存命中": "Cache hit",
    "缺少 Pillow,无法生成测试图": "Pillow missing — cannot build the test image",
    "网络速率": "Network speed",
    "能力靠猜,建议实测一次": "Capability is guessed here — run the test once",
    "能看图(主模型直收)": "Can see images (sent to the main model)",
    "自动化中心": "Automation Center",
    "自动化中心 · Scout Agent": "Automation Center · Scout Agent",
    "视觉模型": "Vision model",
    "识图成文字": "will describe them as text instead",
    "请先选择或填写模型": "Select or enter a model first",
    "请输入插件功能描述": "Enter plugin feature description",
    "请输入插件名称": "Enter plugin name",
    "请输入登录密码以确认插件创建(写码操作需二次验证)": "Enter login password to confirm plugin creation (write operation needs second verification)",
    "调用次数": "Call count",
    "过滤插件": "Filter plugins",
    "运行历史": "Run history",
    "运行时间: 0 小时": "Uptime: 0 hours",
    "运行观测 · Scout Agent": "Run Observer · Scout Agent",
    "运行观测 →": "Run Observer →",
    "运行观测时间线": "Run observer timeline",
    "返回主页": "Back to home",
    "返回插件列表": "Back to plugin list",
    "选择上游触发器…": "Select upstream trigger...",
    "通知中心 · Scout Agent": "Notification Center · Scout Agent",
    "通知将主动推送到这些渠道": "Notifications are actively pushed to these channels",
    "邮件推送 (SMTP)": "Email push (SMTP)",
    "部分图片未识读": "Some images were not read",
    "配置保存成功": "Config saved",
    "配置已重置": "Config reset",
    "配置文件 (config.json)": "Config file (config.json)",
    "配置文件必须是有效的 JSON 格式": "Config file must be valid JSON",
    "配置格式": "Config format",
    "配置格式错误,无法保存": "Invalid config format, cannot save",
    "配置格式错误,无法格式化": "Invalid config format, cannot format",
    "配置模板": "Config template",
    "配置说明": "Config description",
    "配置错误会导致插件无法加载": "Config errors prevent plugins from loading",
    "重新加载失败": "Reload failed",
    "重新加载所有插件失败:": "Failed to reload all plugins:",
    "重新加载插件失败:": "Failed to reload plugin:",
    "重置": "Reset",
    "鉴权失败,无法判定": "Auth failed — cannot determine",
    "问候插件": "Greeting plugin",
    "验证配置": "Validate config",
    "(无内容)": "(empty)",
    "(无描述)": "(no description)",
    "(未记录,可修正配置后重试)": "(not recorded — fix the config and retry)",
    ",事件名填": ", fill in the event name",
    "🌐 网页": "🌐 Web",
    "👁️ 文件监听": "👁️ File Watch",
    "👋 智能问候": "👋 Smart Greeting",
    "💀 死信队列 (DLQ)": "💀 Dead Letter Queue (DLQ)",
    "💡 提示": "💡 Tip",
    "💬 帮助信息": "💬 Help",
    "💻 系统资源": "💻 System Resources",
    "💾 保存插件": "💾 Save Plugin",
    "📄 SKILL.md 内容": "📄 SKILL.md Content",
    "📅 日期查询": "📅 Date Query",
    "📈 CPU 使用历史": "📈 CPU History",
    "📈 内存使用历史": "📈 Memory History",
    "📊 工具监控": "📊 Tool Monitor",
    "📊 模板: 每日自我报告": "📊 Template: Daily Self Report",
    "📊 系统监控": "📊 System Monitor",
    "📋 复制全文": "📋 Copy All",
    "📋 复制命令": "📋 Copy Command",
    "📚 已安装技能": "📚 Installed Skills",
    "📝 描述": "📝 Description",
    "📝 描述插件功能": "📝 Describe Plugin Feature",
    "📡 事件总线": "📡 Event Bus",
    "📦 一键安装": "📦 One-click Install",
    "📦 全网找到": "📦 Found Online",
    "📰 模板: 每日资讯简报": "📰 Template: Daily News Brief",
    "🔄 刷新数据": "🔄 Refresh",
    "🔄 重新加载全部": "🔄 Reload All",
    "🔄 重新生成": "🔄 Regenerate",
    "🔍 先搜全网现成 Skill/插件": "🔍 Search Online for Ready-made Skills/Plugins",
    "🔔 通知中心": "🔔 Notification Center",
    "🔗 Webhook 管理": "🔗 Webhook Management",
    "🔗 查看仓库": "🔗 View Repo",
    "🔧 插件代码": "🔧 Plugin Code",
    "🚫 广告过滤": "🚫 Ad Blocker",
    "🤖 AI 插件生成器": "🤖 AI Plugin Builder",
    "🤖 Agent 状态": "🤖 Agent Status",
    "🧩 插件状态": "🧩 Plugin Status",
    "🧩 插件管理": "🧩 Plugin Management",
    # 模板拆分片段(__tpl/__t 需要的前后缀片段)
    "运行时间: ": "Uptime: ",
    " 分钟": " min",
    " 核心": " cores",
    "总会话数: ": "Total sessions: ",
    "工具类型: ": "Tool types: ",
    "已清空 ": "Cleared ",
    " 条": " entries",
    "✗ 配置格式错误: ": "✗ Invalid config format: ",
    "近7天共": "Last 7 days: ",
    "次运行": " runs",
    "状态": "Status",
    "作用域: ": "Scope: ",
    "目录: ": "Directory: ",
    "路径: ": "Path: ",
    "去抖": "debounce",
    "轮询": "poll",
    "模板": "template",
    "配置": "config",
    " 配置": " Config",
    "· 状态": "· status",
    "版本: ": "Version: ",
    "优先级: ": "Priority: ",
    # 更多模板片段
    "次": " runs",
    " 次": " runs",
    "次 · 最近": " runs · recent",
    "步 ·": " steps ·",
    "运行时间:": "Uptime:",
    "分钟": " min",
    "核心": " cores",
    "总会话数:": "Total sessions:",
    "工具类型:": "Tool types:",
    "成功率": "Success rate",
    "24h 对话": "24h conversations",
    "成本": "Cost",
    "⚡缓存": "⚡ cached",
    "确定要卸载技能「": "Uninstall skill 「",
    "」吗?": "」?",
    "将删除 $SCOUT_DATA_DIR/skills/": "will delete $SCOUT_DATA_DIR/skills/",
    " 目录": " directory",
    "✗ 配置格式错误:": "✗ Invalid config format:",
    "重新输入后可自动记忆": "re-enter to auto-remember",
    "本地无记忆": "no local memory",
    "已复制": "copied",
    "目录": "directory",
    "敏感词": "sensitive word",
    # ── 管理页卡片混合短语(第4轮补充) ──
    "触发 ": "Triggered ",
    " 次运行": " runs",
    "近7天共 ": "Last 7 days: ",
    " 步": " steps",
    " 次工具调用": " tool calls",
    "执行事件流(": "Event flow (",
    " 条)": " items)",
    "验证证据(": "Verification evidence (",
    ")": ")",
    "通过": "passed",
    "未通过": "failed",
    "最近 ": "recent ",
    "事件 ": "Event ",
    "上游 ": "Upstream ",
    "运行": "Run",
    "停用": "Disable",
    "递归": "Recursive",
    "仅顶层": "Top-level only",
    "轮询 ": "Poll every ",
    "去抖 ": "Debounce ",
    "✕ 删除": "✕ Delete",
    "新增": "Add",
    "修改": "Modify",
    "调用 ": "Called ",
    " 次 · 最近 ": " times · recent ",
    "复制 URL": "Copy URL",
    "(未命名)": "(unnamed)",
    "事件载荷": "Event payload",
    "已推送": "Pushed",
    "未推送": "Not pushed",
    "已过滤": "Filtered",
    "📧 邮件✓": "📧 Email✓",
    "24h 对话 ": "24h conversations ",
    "成功率 ": "Success rate ",
    "输出:": "Output:",
    "输入:": "Input:",
    "错误: ": "Error: ",
    "状态 ": "Status ",
    "(空)": "(empty)",
    "(无消息)": "(no message)",
    "无 span 数据": "No span data",
    "技能": "Skill",
    "👁️ 详情": "👁️ Details",
    "已启用": "Enabled",
    "暂无描述": "No description",
    "作者: ": "Author: ",
    "关键词: ": "Keywords: ",
    "正则: ": "Regex: ",
    "已加载 ": "Loaded ",
    " 模板": " template",
    # ── index.html 静态残留(第4轮补充) ──
    "恢复": "Restore",
    "拒绝": "Rejected",
    "执行中...": "Running...",
    "内容:": "Content:",
    "字段": "field",
    "键": "key",
    "响应": "response",
    "否": "No",
    "是": "Yes",
    # ── index.html 动态 alert/confirm/toast(第5轮补充) ──
    "请填写 ": "Please fill in ",
    "启动失败: ": "Start failed: ",
    "启动失败,请重试": "Start failed, please retry",
    "确定要停止渠道 \"": "Stop channel \"",
    "\" 吗?": "\"? ",
    "确定要删除渠道 \"": "Delete channel \"",
    "\" 吗?此操作不可撤销。": "\"? This action cannot be undone.",
    " 超过 20MB 限制": " exceeds the 20MB limit",
    "确定删除「": "Delete \"",
    "已自动识别 ": "Auto-detected ",
    " 个目标": " targets",
    "确定要删除任务 \"": "Delete task \"",
    "确定要删除知识页面 \"": "Delete knowledge page \"",
    "天": "d",
    "小时": "h",
    "次运行/天": "runs/day",
    "最高": "max",
    # ── index.html 渲染器混合模板(第6轮补充) ──
    "任务分解为 ": "Task breakdown: ",
    " 个子任务,并行委派:": " subtasks, delegating in parallel: ",
    "子任务 ": "Subtask ",
    "执行工具 ": "Running tool ",
    "步骤 ": "Step ",
    " (累计 ": " (cumulative ",
    "缓存 ": "Cache ",
    "✅ 完成 · ": "✅ Done · ",
    "步": " steps",
    "❌ 错误: ": "❌ Error: ",
    "未找到「": "No results for \"",
    "」相关内容": "\"",
    "调度器: ": "Scheduler: ",
    "... 还有 ": "... and ",
    "凝萃失败: ": "Distillation failed: ",
    "当前登录用户:": "Current user: ",
    "(密码已设置,修改后立即生效)": " (password set, effective immediately)",
    " 个工具 ": " tools ",
    " 页": " pages",
    " 字符": " characters",
    ") 解析 ": ") parsing ",
    "✅ 成功导入 ": "✅ Imported ",
    " 个文件:": " files: ",
    "部分成功:": "Partial success: ",
    " 个文件已导入": " files imported",
    "⚠️ 渲染失败: ": "⚠️ Render failed: ",
    "搜索 \"": "Search \"",
    "\" 找到 ": "\" found ",
    " 条结果:": " results: ",
    "· 有新版本 ": "· New version ",
    "标记完成": "Mark complete",
    "重新激活": "Reactivate",
    "暂无 MCP 服务器": "No MCP servers",
    "最后访问": "Last accessed",
    "最低": "min",
    "调用": "calls",
    "花费": "spent",
    # ── index.html textContent 动态文本(第6轮补充) ──
    "⏳ 运行中...": "⏳ Running...",
    "⚡ 无委派子任务(主代理直接执行)": "⚡ No delegated subtasks (main agent executes directly)",
    "✅ 完成": "✅ Done",
    "✅ 已完成": "✅ Completed",
    "❌ 失败": "❌ Failed",
    "❌ 请先填写自定义模型名或选择预设模型": "❌ Please enter a custom model name or select a preset model",
    "两次输入的密码不一致": "Passwords do not match",
    "完成": "Done",
    "密码长度至少 6 位": "Password must be at least 6 characters",
    "已保存": "Saved",
    "未保存": "Unsaved",
    "未知错误": "Unknown error",
    "网络错误": "Network error",
    "请输入用户名": "Please enter a username",
    "首次使用,请设置账户": "First time use, please set up an account",
    # ── index.html prompt 对话框(第6轮补充) ──
    "目标标题:": "Target title:",
    "目标描述 (可选):": "Target description (optional):",
    "任务标题:": "Task title:",
    "任务描述 (可选):": "Task description (optional):",
    "请输入搜索关键词:": "Enter a search keyword:",
    # ── index.html 渲染器动态模板/静态文本(第7轮补充) ──
    "血缘加载失败: ": "Lineage load failed: ",
    "进度: ": "Progress: ",
    "任务: ": "Tasks: ",
    "创建: ": "Created: ",
    "耗时: ": "Duration: ",
    "消息: ": "Message: ",
    "状态: ": "Status: ",
    "执行步骤 (": "Execution steps (",
    "步骤: ": "Step: ",
    "预算: ": "Budget: ",
    "工具: ": "Tools: ",
    "调度: ": "Schedule: ",
    " | 上次运行: ": " | Last run: ",
    "新会话标题": "New session title",
    "血缘关系": "Lineage",
    "分叉": "Branch",
    "工具配置": "Tool configuration",
    "登录认证": "Login authentication",
    "启用登录密码": "Enable login password",
    "确认密码": "Confirm password",
    "搜索引擎源(支持多个)": "Search engine sources (multiple)",
    "配置至少一个启用的源后": "After configuring at least one enabled source",
    "编排中...": "Orchestrating...",
    "无血缘记录": "No lineage records",
    "子分支:": "Child branch:",
    "向量模型": "Vector model",
    "拖拽文件到此处,或": "Drag files here, or",
    "点击选择": "click to select",
    "导入成功": "Import successful",
    "导入失败": "Import failed",
    "构建知识图谱...": "Building knowledge graph...",
    "文档": "Document",
    "数据": "Data",
    "未修改": "Unmodified",
    "已注册工具": "Registered tools",
    "暂无 Webhook": "No webhooks",
    "插件": "Plugin",
    "强制提取": "Force extraction",
    "总成本": "Total cost",
    "最近追踪": "Recent traces",
    "尚未运行过凝萃": "Distillation has not run yet",
    "开启后访问 Web 界面需要输入用户名和密码登录;默认关闭": "When enabled, accessing the Web UI requires a username and password login; disabled by default",
    "最小对话数(低于此数跳过提取)": "Minimum conversations (below this, extraction is skipped)",
    "支持 PDF、DOCX、Markdown、TXT、CSV、JSON、HTML、代码文件": "Supports PDF, DOCX, Markdown, TXT, CSV, JSON, HTML, and code files",
    "用于\"回退到某条消息再分叉\":只复制到指定序号为止,之后从分支重新开始。": "For \"branching by reverting to a message\": only copies up to the specified sequence number, then restarts from the branch.",
    # ── index.html 属性 title/placeholder(第7轮补充) ──
    "任务与追踪": "Tasks & Tracking",
    "工具与监控": "Tools & Monitoring",
    "切换主题": "Toggle theme",
    "切换界面语言 / Toggle UI language": "Toggle UI language",
    "留空则自动生成 (会话标题 + fork)": "Leave empty to auto-generate (session title + fork)",
    "留空 = 复制全部消息": "Leave empty = copy all messages",
    "输入自定义模型名,回车确认(Esc 取消)": "Enter custom model name, press Enter to confirm (Esc to cancel)",
    "登录用户名": "Login username",
    "至少 6 位": "At least 6 characters",
    "再次输入密码": "Re-enter password",
    "复制": "Copy",
    "重新生成": "Regenerate",
    "输入要记住的内容...": "Enter content to remember...",
    "类别标签 (可选)": "Category label (optional)",
    "加载选中页面": "Load selected page",
    "新建页面": "New page",
    "标题": "Heading",
    "粗体": "Bold",
    "斜体": "Italic",
    "代码": "Code",
    "链接": "Link",
    "列表": "List",
    "引用": "Quote",
    "在这里输入 Markdown 内容...": "Enter Markdown content here...",
    "名称": "Name",
    "删除此源": "Delete this source",
    "命令": "Command",
    "任务描述,如:总结最新消息": "Task description, e.g. summarize the latest messages",
    "名称,如 本地 SearXNG": "Name, e.g. local SearXNG",
    "URL(留空用默认端点;Google 需带 ?cx=你的ID)": "URL (leave empty for default endpoint; Google needs ?cx=yourID)",
    "API Key(可选;searxng / duckduckgo 无需)": "API Key (optional; not needed for searxng / duckduckgo)",
    "启用": "Enable",
    "重要性": "Importance",
    "访问次数": "Access count",
    "创建时间": "Created",
    "查看详情": "View details",
    "删除": "Delete",
    "删除任务": "Delete task",
    "删除目标": "Delete goal",
    "点击查看大图": "Click to view full image",
    "编辑": "Edit",
    "分叉此会话": "Fork this session",
    "编辑标题": "Edit title",
    "删除会话": "Delete session",
    "对话": "conversations",
    "输出": "output",
    "输入": "input",
    "摘要": "summary",
    "详情": "details",
    "查看": "view",
    "安装": "install",
    "卸载": "uninstall",
    "禁用": "disable",
    "保存": "save",
    "取消": "cancel",
    "确认": "confirm",
    "重试": "retry",
    "刷新": "refresh",
    "加载中...": "Loading...",
    "暂无数据": "No data",
    "加载失败": "Load failed",
    "加载失败: ": "Load failed: ",
    "请求失败": "Request failed",
    "成功": "success",
    "失败": "failed",
    "等待中": "pending",
    "进行中": "running",
    "已完成": "completed",
    "已终止": "terminated",
    "已跳过": "skipped",
    "触发": "trigger",
    "执行": "execute",
    "上次执行": "last run",
    "下次执行": "next run",
    "启用状态": "enabled",
    "更新时间": "updated",
    "事件名称": "event name",
    "事件数据": "event data",
    "事件处理": "event processing",
    "推送": "push",
    "通知": "notify",
    "忽略": "ignore",
    "测试": "test",
    "提交": "submit",
    "预览": "preview",
    "已复制到剪贴板": "copied to clipboard",
    "确定": "OK",
    "正在加载": "Loading",
    "搜索": "search",
    "清空": "clear",
    "全部": "all",
    "描述": "description",
    "类型": "type",
    "操作": "actions",
    "数量": "count",
    "时间": "time",
    "日期": "date",
    "内容": "content",
    "路径": "path",
    "文件": "file",
    "大小": "size",
    "在线": "online",
    "离线": "offline",
    # watcher / plugin-builder / plugin-config 补充
    "添加": "Add",
    "添加失败: ": "Add failed: ",
    "已添加监听目录": "Watch directory added",
    "- 是否启用插件": "- whether the plugin is enabled",
    "- 优先级 (0-100)": "- priority (0-100)",
    "- 关键词列表": "- keyword list",
    "- 响应映射": "- response mapping",
    "」吗?\n将删除 $SCOUT_DATA_DIR/skills/": "」? \nwill delete $SCOUT_DATA_DIR/skills/",
    "1. 可以编辑生成的代码进行自定义调整": "1. You can edit the generated code for customization",
    "2. 插件名称只能包含小写字母、数字和下划线": "2. Plugin name may only contain lowercase letters, digits and underscores",
    "3. 保存后的插件会自动启用,可在插件管理页面查看": "3. Saved plugins auto-enable; view them in Plugin Management",
    "该会话没有追踪数据": "No trace data for this session",
    " 轮": " rounds",
    "轮": " rounds",
    "删除定时任务「": "Delete scheduled task 「",
    "」?": "」?",
    "(成功率": "(success rate",
    "%)": "%)",
    "运行结果: ": "Run result: ",
    # ── 管理页(automation/notify/plugins/watcher/webhooks/events)第8轮补充 ──
    "名称,如: PR审查": "Name, e.g. PR review",
    "事件名,如: tool.complete / file": "Event name, e.g. tool.complete / file",
    "过滤器JSON(可选),如 {\"source\":\"cron:*\"}": "Filter JSON (optional), e.g. {\"source\":\"cron:*\"}",
    "任务模板,支持 {{event.xxx}} 占位符。如: 处理新文件 {{event.file_path}}": "Task template supporting {{event.xxx}} placeholders. E.g. handle new file {{event.file_path}}",
    "验证规则JSON(可选),如 [{\"type\":\"contains\",\"value\":[\"完成\"]}]": "Verify rule JSON (optional), e.g. [{\"type\":\"contains\",\"value\":[\"done\"]}]",
    "冷却秒数(防抖),默认0": "Cooldown seconds (debounce), default 0",
    "调度,如: 每30分钟 / 每天09:00": "Schedule, e.g. every 30 minutes / daily 09:00",
    "任务描述(给 Agent 执行)": "Task description (for Agent to execute)",
    "收件人邮箱": "Recipient email",
    "SMTP 主机 (如 smtp.qq.com)": "SMTP host (e.g. smtp.qq.com)",
    "端口 (465)": "Port (465)",
    "SMTP 账号": "SMTP account",
    "SMTP 密码/授权码": "SMTP password / auth code",
    "渠道名 (如 telegram)": "Channel name (e.g. telegram)",
    "目标 ID / 群组": "Target ID / group",
    "例如:创建一个插件,当用户说“帮助”时显示帮助信息": "E.g. create a plugin that shows help when the user says \"help\"",
    "插件名称(小写字母和下划线,如:my_plugin)": "Plugin name (lowercase letters and underscores, e.g. my_plugin)",
    "生成的代码将显示在这里...": "Generated code will be shown here...",
    "绝对路径,如 /data/inbox": "Absolute path, e.g. /data/inbox",
    "名称(可选,如 部署通知)": "Name (optional, e.g. deploy notification)",
    "任务描述(必填)——收到请求时 Agent 执行的任务。支持 {{event.xxx}} 占位符引用请求体字段。": "Task description (required) - task executed by Agent when request received. Supports {{event.xxx}} placeholders referencing request body fields.",
    "重新加载": "Reload",
    "查看配置": "View config",
    "编辑配置": "Edit config",
    "测试触发": "Test trigger",
    "删除该触发器?": "Delete this trigger?",
    "确认移除该监听目录?": "Remove this watch directory?",
    "确认删除该 Webhook?": "Delete this Webhook?",
    "确认清空死信队列?": "Clear dead-letter queue?",
    "确认清空全部推送历史?": "Clear all push history?",

    # ── index.html 多模型类型独立厂商(第9轮补充) ──
    "主服务商": "Main provider",
    "跟随主服务商": "Follow main provider",
    "(可选独立服务商)": "(optional independent provider)",
    "(文本对话与工具调用)": "(text chat & tool use)",
    "(使用主服务商的 API Key)": "(uses main provider's API Key)",

    # ── 服务商集中配置(第10轮补充) ──
    "服务商 API 配置": "Provider API Config",
    "(集中配置各服务商的 API Key 与 Base URL)": "(Centralized API Key & Base URL config for all providers)",
    "保存服务商配置": "Save Provider Config",
    "在此统一配置各服务商的 API Key 与 Base URL,文本/视觉/图像模型模块只需选择服务商和模型即可自动使用对应凭据。": "Configure API Keys & Base URLs for all providers here; the text/vision/image sections only need to select a provider and model.",
    "✅ 该服务商已配置 API Key": "✅ API Key configured for this provider",
    "⚠️ 该服务商未配置 API Key,请在上方\"服务商 API 配置\"中填写": "⚠️ No API Key for this provider; fill it in the \"Provider API Config\" section above",
    "已保存,留空则不修改": "Saved; leave blank to keep unchanged",
    "Base URL": "Base URL",
    "未填写任何服务商凭据": "No provider credentials entered",
    "服务商配置已保存": "Provider config saved",
    " 个已保存,": " saved, ",
    " 个失败": " failed",
    "已配置": "Configured",
}

def esc(s):
    # 转义为 JS 字符串字面量:\\ 与 " 之外,还必须把真实换行/回车/制表符转为 \\n \\r \\t 字面量,
    # 否则多行文本会生成真实换行导致 i18n.js 语法错误(SyntaxError: Invalid or unexpected token)
    return (s.replace('\\', '\\\\')
             .replace('"', '\\"')
             .replace('\r\n', '\\n')
             .replace('\n', '\\n')
             .replace('\r', '\\n')
             .replace('\t', '\\t'))

def build_block(entries):
    lines = []
    for zh, en in sorted(entries.items(), key=lambda x: x[0]):
        lines.append(f'    "{esc(zh)}": {{ zh: "{esc(zh)}", en: "{esc(en)}" }},')
    return "\n".join(lines)

def assert_no_duplicate_keys():
    """校验 M 无重复键,有则打印冲突明细并以非零码退出。

    为什么必须显式检测:Python 的 dict 字面量遇到重复键**静默取最后一个**、
    不报错也不告警(ruff F601 能查,但只有把 scripts/ 纳入阻断才真正拦得住)。
    本表一旦静默覆盖,产物 i18n.js 的英文文案会随「哪条写在后面」而变,
    属于典型的看不见的回归。这里用 AST 解析本文件自身的 M 字面量,
    不维护并行清单、因此不会与数据脱节。
    """
    import ast
    from collections import Counter, defaultdict

    tree = ast.parse(open(__file__, encoding="utf-8").read(), filename=__file__)
    literal = None
    for node in ast.walk(tree):
        if isinstance(node, ast.Assign):
            for tgt in node.targets:
                if isinstance(tgt, ast.Name) and tgt.id == "M" and isinstance(node.value, ast.Dict):
                    literal = node.value
    if literal is None:
        print("⚠️ 未在本文件中找到 M 字面量,跳过重复键校验")
        return

    seen = defaultdict(list)  # zh -> [(lineno, en), ...]
    for k, v in zip(literal.keys, literal.values):
        if isinstance(k, ast.Constant) and isinstance(v, ast.Constant):
            seen[k.value].append((k.lineno, v.value))
    dups = {zh: occ for zh, occ in seen.items() if len(occ) > 1}
    if not dups:
        return

    print(f"❌ M 中存在 {len(dups)} 个重复键(dict 字面量会静默取最后一个值):")
    for zh in sorted(dups):
        occs = ", ".join(f'L{n}="{v}"' for n, v in dups[zh])
        kept = dups[zh][-1][1]
        print(f'  "{zh}" 出现 {len(dups[zh])} 次 -> 实际生效 "{kept}" | {occs}')
    print("请删除重复条目或统一取值后再运行本脚本。")
    sys.exit(1)

def existing_keys(src):
    """解析 i18n.js 已有的 zh 键集合,还原 JS 转义后返回(与 M 的键同形)。

    两处都得做对,否则已有键会被误判为"新键"、每跑一次就多插一份(实测曾把
    10 份 `\" 吗?` 累积成 11 份):
      1) 捕获要能吃下转义引号 —— 旧写法 `"([^"]+)"` 遇到 `"…\\" 吗?"` 这类
         含 `\\"` 的键会整条漏掉;
      2) 比较前要反转义 —— 文件里是 esc() 产出的 JS 形态(`\\"` `\\n` `\\\\`),
         而 M 的键是 Python 原始字符串,不还原则永不相等。
    文件形态本身即合法 JSON 字符串字面量,故用 json.loads 还原最稳。
    """
    out = set()
    for m in re.finditer(r'^    "((?:[^"\\]|\\.)*)":\s*\{\s*zh:', src, re.M):
        raw = m.group(1)
        try:
            out.add(json.loads('"' + raw + '"'))
        except Exception:
            out.add(raw)  # 兜底:极端手写条目按原样比较(宁可不插,也不重复插)
    return out


def report_existing_dups(src):
    """报告 i18n.js 内已存在的重复键(JS 对象重复键同样是后者静默覆盖)。"""
    from collections import Counter

    keys = []
    for m in re.finditer(r'^    "((?:[^"\\]|\\.)*)":\s*\{\s*zh:', src, re.M):
        raw = m.group(1)
        try:
            keys.append(json.loads('"' + raw + '"'))
        except Exception:
            keys.append(raw)
    dups = {k: n for k, n in Counter(keys).items() if n > 1}
    if dups:
        print(f"⚠️ i18n.js 已存在 {len(dups)} 个重复键(JS 会静默取最后一个值),"
              f"最多者:{sorted(dups.items(), key=lambda x: -x[1])[:3]}")
    return dups


def main():
    assert_no_duplicate_keys()
    path = "scout/web/static/i18n.js"
    src = open(path, encoding="utf-8").read()
    report_existing_dups(src)
    # 已有键(还原转义后比较,避免把已存在的转义引号键当成新键重复插入)
    have = existing_keys(src)
    new = {zh: en for zh, en in M.items() if zh not in have}
    if not new:
        print("无新增条目")
        return
    block = "\n" + build_block(new) + "\n"
    # 插入到字典结束 }; 前(最后一条 "⚠️ 未配置 API Key" 之后)
    marker = '"⚠️ 未配置 API Key": { zh: "⚠️ 未配置 API Key", en: "⚠️ API Key not configured" },'
    if marker not in src:
        print("未找到插入锚点")
        sys.exit(1)
    src = src.replace(marker, marker + block, 1)
    open(path, "w", encoding="utf-8").write(src)
    print(f"已插入 {len(new)} 条新字典条目")

if __name__ == "__main__":
    main()