#!/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()