From f441d0cb1220e3d945e426c26647d5c8afa0966e Mon Sep 17 00:00:00 2001 From: oiupoyt Date: Sun, 11 Oct 2026 17:25:20 +0530 Subject: [PATCH] feat: add immediately: true, TreeWalker engine, and comprehensive 500+ term DSH dictionary --- lib/client.js | 366 ++++++++++++++++++++++++++------------- lib/client.js.map | 4 +- package.json | 5 +- src/client/dictionary.ts | 216 ++++++++++++++++------- src/client/index.ts | 193 +++++++++++++-------- 5 files changed, 514 insertions(+), 270 deletions(-) diff --git a/lib/client.js b/lib/client.js index fb1fdde..ad90be6 100644 --- a/lib/client.js +++ b/lib/client.js @@ -22,147 +22,229 @@ var index_exports = {}; __export(index_exports, { apply: () => apply, inject: () => inject, - name: () => name + name: () => name, + translateElement: () => translateElement, + translateText: () => translateText }); module.exports = __toCommonJS(index_exports); // src/client/dictionary.ts var DICTIONARY = { - // Navigation & Core Concepts + // DSH Marketplace & Store + "\u63D2\u4EF6\u5E02\u573A": "Plugin Marketplace", + "\u901B\u4E00\u901B\uFF0C\u70B9\u4E00\u4E0B\uFF0C\u88C5\u597D\u3002": "Browse, click, and install.", + "\u5168\u90E8\u63D2\u4EF6": "All Plugins", + "\u5DF2\u5B89\u88C5\u63D2\u4EF6": "Installed Plugins", + "\u63D2\u4EF6": "Plugins", + "\u5E02\u573A": "Marketplace", + "\u70ED\u95E8\u63D2\u4EF6": "Popular Plugins", + "\u6700\u65B0\u4E0A\u67B6": "Recently Added", + "\u63A8\u8350\u63D2\u4EF6": "Recommended Plugins", + "\u4E0B\u8F7D\u91CF": "Downloads", + "\u8BC4\u5206": "Rating", + "\u4F5C\u8005": "Author", + "\u5B89\u88C5\u91CF": "Installs", + "\u5B89\u88C5\u63D2\u4EF6": "Install Plugin", + "\u5378\u8F7D\u63D2\u4EF6": "Uninstall Plugin", + "\u66F4\u65B0\u63D2\u4EF6": "Update Plugin", + "\u7B2C\u4E09\u65B9\u63D2\u4EF6": "Third-Party Plugins", + "\u5B98\u65B9\u63D2\u4EF6": "Official Plugins", + "\u641C\u7D22\u63D2\u4EF6": "Search plugins...", + "\u641C\u7D22\u63D2\u4EF6\u540D\u79F0\u6216\u4F5C\u8005": "Search by plugin name or author...", + "\u4E00\u952E\u5B89\u88C5": "Install", + "\u4E00\u952E\u66F4\u65B0": "Update", + "\u6B63\u5728\u4E0B\u8F7D": "Downloading...", + "\u6B63\u5728\u5B89\u88C5": "Installing...", + "\u5B89\u88C5\u5B8C\u6210": "Installed", + "\u5378\u8F7D\u5B8C\u6210": "Uninstalled", + // Skills Manager Navigation & Tabs "\u6280\u80FD\u7BA1\u7406": "Skills Manager", "\u6280\u80FD\u7BA1\u7406\u5668": "Skills Manager", + "\u7EDF\u4E00\u52A0\u8F7D\u548C\u7BA1\u7406\u672C\u673A Agent Skills\u3002": "Load and manage Agent Skills on this machine in one place.", "\u6280\u80FD\u5217\u8868": "Skills List", "\u5DF2\u5B89\u88C5\u6280\u80FD": "Installed Skills", "\u53EF\u7528\u6280\u80FD": "Available Skills", "\u53D1\u73B0\u6280\u80FD": "Discover Skills", - "\u4ED3\u5E93\u8BA2\u9605": "Repository Subscriptions", + "\u4ED3\u5E93\u8BA2\u9605": "Repositories", "\u4ED3\u5E93\u7BA1\u7406": "Repositories", "\u6280\u80FD\u6765\u6E90": "Skill Sources", "\u5168\u5C40\u6280\u80FD": "Global Skills", "\u9879\u76EE\u6280\u80FD": "Project Skills", + "\u516C\u5171 Agent": "Shared Agent", + "\u9879\u76EE Agent": "Project Agent", + "\u9879\u76EE Skills": "Project Skills", "\u4F1A\u8BDD\u6280\u80FD": "Session Skills", "\u56DE\u6536\u7AD9": "Trash", "\u8BCA\u65AD\u5F02\u5E38": "Diagnostics", "\u5F02\u5E38\u8BCA\u65AD": "Diagnostics", + "\u6E90\u6587\u4EF6\u53EA\u8BFB": "Read-only", + "\u53EA\u8BFB": "Read-only", + "\u521B\u5EFA\u4F4D\u7F6E": "Location", + "\u5168\u5C40 DSH": "Global DSH", + "\u9879\u76EE DSH": "Project DSH", + "DSH \u6280\u80FD": "DSH Skills", // Actions & Buttons + "\u521B\u5EFA\u6280\u80FD": "Create Skill", + "\u521B\u5EFA\u65B0\u6280\u80FD": "Create Skill", + "\u65B0\u5EFA\u6280\u80FD": "New Skill", + "\u7ACB\u5373\u521B\u5EFA": "Create Now", + "\u5BFC\u5165\u6280\u80FD": "Import Skill", + "\u5BFC\u51FA\u6280\u80FD": "Export Skill", + "\u5BFC\u5165": "Import", + "\u5BFC\u51FA": "Export", + "\u5237\u65B0": "Refresh", "\u68C0\u67E5\u66F4\u65B0": "Check Updates", "\u6B63\u5728\u68C0\u67E5\u66F4\u65B0\u2026": "Checking for updates...", "\u6B63\u5728\u68C0\u67E5\u66F4\u65B0...": "Checking for updates...", "\u68C0\u67E5\u5E76\u66F4\u65B0": "Check & Update", "\u66F4\u65B0": "Update", - "\u5DF2\u662F\u6700\u65B0": "Up to date", - "\u53EF\u66F4\u65B0": "Update Available", + "\u7BA1\u7406\u66F4\u65B0": "Manage Updates", "\u7ACB\u5373\u66F4\u65B0": "Update Now", "\u786E\u8BA4\u66F4\u65B0": "Confirm Update", "\u56DE\u6EDA": "Rollback", "\u786E\u8BA4\u56DE\u6EDA": "Confirm Rollback", "\u6062\u590D": "Restore", - "\u5220\u9664": "Delete", - "\u6C38\u4E45\u5220\u9664": "Permanently Delete", - "\u6E05\u7A7A\u56DE\u6536\u7AD9": "Empty Trash", + "\u79FB\u5230\u56DE\u6536\u7AD9": "Move to Trash", "\u79FB\u5165\u56DE\u6536\u7AD9": "Move to Trash", - "\u5BFC\u5165": "Import", - "\u5BFC\u5165\u6280\u80FD": "Import Skill", - "\u5BFC\u51FA": "Export", - "\u5BFC\u51FA\u6280\u80FD": "Export Skill", - "\u65B0\u5EFA\u6280\u80FD": "New Skill", - "\u521B\u5EFA\u6280\u80FD": "Create Skill", - "\u5237\u65B0": "Refresh", + "\u6C38\u4E45\u5220\u9664": "Delete Forever", + "\u6E05\u7A7A\u56DE\u6536\u7AD9": "Empty Trash", + "\u5220\u9664": "Delete", "\u542F\u7528": "Enable", "\u505C\u7528": "Disable", "\u5DF2\u542F\u7528": "Enabled", "\u5DF2\u505C\u7528": "Disabled", "\u5168\u90E8\u542F\u7528": "Enable All", "\u5168\u90E8\u505C\u7528": "Disable All", + "\u542F\u505C\u6765\u6E90": "Toggle Source", + "\u542F\u505C\u6280\u80FD": "Toggle Skill", "\u5B89\u88C5": "Install", "\u5DF2\u5B89\u88C5": "Installed", "\u5378\u8F7D": "Uninstall", - "\u67E5\u770B": "View", "\u67E5\u770B\u8BE6\u60C5": "View Details", "\u67E5\u770B\u8BF4\u660E": "View Docs", - "\u4FDD\u5B58": "Save", + "\u67E5\u770B": "View", + "\u66F4\u591A": "More", "\u53D6\u6D88": "Cancel", + "\u4FDD\u5B58": "Save", "\u5173\u95ED": "Close", "\u786E\u5B9A": "OK", "\u590D\u5236": "Copy", + "\u5DF2\u590D\u5236": "Copied", "\u590D\u5236\u6210\u529F": "Copied", "\u641C\u7D22": "Search", "\u91CD\u8BD5": "Retry", - // Status & Counts - "\u5168\u90E8": "All", + "\u9009\u62E9\u6587\u4EF6": "Choose File", + "\u9009\u62E9\u6587\u4EF6\u5939": "Choose Folder", + "\u95EE\u9898\u53CD\u9988": "Issues", + "\u8BBE\u7F6E": "Settings", + // Status & Filters + "\u5168\u90E8\u6765\u6E90": "All Sources", + "\u5168\u90E8\u72B6\u6001": "All Statuses", + "\u8C03\u7528\u72B6\u6001": "Invocation Status", "\u72B6\u6001": "Status", - "\u7C7B\u578B": "Type", + "\u6765\u6E90": "Source", + "\u88AB\u8986\u76D6": "Shadowed", "\u540D\u79F0": "Name", "\u7248\u672C": "Version", - "\u64CD\u4F5C": "Actions", - "\u6765\u6E90": "Source", - "\u8DEF\u5F84": "Path", - "\u63CF\u8FF0": "Description", - "\u8BE6\u60C5": "Details", "\u5927\u5C0F": "Size", + "\u7C7B\u578B": "Type", + "\u64CD\u4F5C": "Actions", + "\u8BE6\u60C5": "Details", + "\u63CF\u8FF0": "Description", + "\u7B80\u4ECB": "Description", + "\u6B63\u6587": "Body", + "\u6E90\u6587\u4EF6": "Source File", "\u66F4\u65B0\u65F6\u95F4": "Updated", "\u521B\u5EFA\u65F6\u95F4": "Created", - "\u4E2A\u6280\u80FD": " skills", - "\u4E2A\u5DF2\u542F\u7528": " enabled", - "\u4E2A\u5DF2\u505C\u7528": " disabled", - "\u4E2A\u88AB\u8986\u76D6": " shadowed", - "\u4E2A\u5F85\u5904\u7406\u6280\u80FD": " pending skills", - "\u4E2A\u8BCA\u65AD\u5F02\u5E38": " diagnostic issues", - // Form Fields & Hints - "\u6280\u80FD\u540D\u79F0": "Skill Name", - "\u6280\u80FD\u63CF\u8FF0": "Description", - "\u6280\u80FD\u5185\u5BB9": "Skill Body", - "\u6280\u80FD\u6B63\u6587": "Skill Body", - "\u4ED3\u5E93\u5730\u5740": "Repository URL", - "\u641C\u7D22\u6280\u80FD": "Search skills...", - "\u641C\u7D22\u6280\u80FD\u540D\u79F0\u3001\u63CF\u8FF0\u6216\u6765\u6E90": "Search skills by name, description, or source...", - "\u8BF7\u8F93\u5165\u4ED3\u5E93\u5730\u5740": "Enter repository URL...", - "\u6CA1\u6709\u627E\u5230\u5339\u914D\u7684\u6280\u80FD": "No matching skills found", - "\u6682\u65E0\u6280\u80FD": "No skills available", - "\u56DE\u6536\u7AD9\u4E3A\u7A7A": "Trash is empty", + "\u5220\u9664\u4E8E": "Deleted at", + "\u6765\u6E90\uFF1A": "Source: ", + // Empty & Loading States + "\u6B63\u5728\u52A0\u8F7D\u6280\u80FD\u2026": "Loading skills...", + "\u6B63\u5728\u52A0\u8F7D\u6280\u80FD...": "Loading skills...", + "\u6B63\u5728\u52A0\u8F7D\u2026": "Loading...", + "\u6B63\u5728\u52A0\u8F7D...": "Loading...", "\u52A0\u8F7D\u4E2D\u2026": "Loading...", "\u52A0\u8F7D\u4E2D...": "Loading...", "\u6B63\u5728\u8BFB\u53D6\u8BF4\u660E\u2026": "Loading documentation...", - // Settings & Configuration + "\u6B63\u5728\u8BFB\u53D6\u8BF4\u660E...": "Loading documentation...", + "\u6CA1\u6709\u5339\u914D\u7684\u6280\u80FD\u3002": "No matching skills.", + "\u6CA1\u6709\u5339\u914D\u7684\u6280\u80FD": "No matching skills", + "\u8BE5\u6765\u6E90\u76EE\u5F55\u4E0D\u5B58\u5728\u6216\u6682\u65F6\u6CA1\u6709\u6280\u80FD\u3002": "This source directory does not exist or has no skills.", + "\u56DE\u6536\u7AD9\u4E3A\u7A7A\u3002": "Trash is empty.", + "\u56DE\u6536\u7AD9\u4E3A\u7A7A": "Trash is empty", + "\u6682\u65E0\u6280\u80FD": "No skills available", + "\u6682\u65E0\u6570\u636E": "No data available", + "\u672A\u63D0\u4F9B\u7B80\u4ECB": "No description provided", + "\u672A\u53D1\u73B0\u8BCA\u65AD\u95EE\u9898\u3002": "No diagnostic issues found.", + // Upload & Import Modal + "\u62D6\u62FD\u6280\u80FD\u5230\u6B64\u5904": "Drop skill here", + "\u652F\u6301 .zip\u3001\u6280\u80FD\u6587\u4EF6\u5939\u6216\u5355\u4E2A SKILL.md": "Supports .zip, a skill folder, or single SKILL.md", + "\u6587\u4EF6\u8981\u6C42": "Requirements", + "\u538B\u7F29\u5305\u6216\u6587\u4EF6\u5939\u9700\u5305\u542B SKILL.md": "Archives or folders must contain SKILL.md", + "SKILL.md \u9700\u5305\u542B YAML \u683C\u5F0F\u7684\u6280\u80FD\u540D\u79F0\u548C\u63CF\u8FF0": "SKILL.md must include YAML name and description", + "\u5BFC\u5165\u65F6\u590D\u5236\u5B8C\u6574\u5185\u5BB9\uFF0C\u4E0D\u4FEE\u6539\u539F\u59CB\u6765\u6E90": "Import copies full content without modifying source", + "\u5BFC\u5165\u5230\u5168\u5C40 DSH": "Import to global DSH", + "\u79FB\u9664\u6240\u9009\u5185\u5BB9": "Remove selection", + "\u8BF7\u9009\u62E9 .zip \u6216\u5355\u4E2A SKILL.md\u3002": "Please choose a .zip or single SKILL.md.", + "\u6240\u9009\u6587\u4EF6\u5939\u4E2D\u6CA1\u6709\u627E\u5230 SKILL.md\u3002": "No SKILL.md found in the selected folder.", + // Creation Form + "\u4F8B\u5982 code-review-helper": "e.g. code-review-helper", + "\u4E00\u53E5\u8BDD\u8BF4\u660E\u4EC0\u4E48\u65F6\u5019\u4F7F\u7528": "One sentence describing when to use it", + "\u6B63\u6587\uFF08Markdown\uFF09": "Body (Markdown)", + "\u5199\u4E0B\u6280\u80FD\u8981\u9075\u5FAA\u7684\u6307\u4EE4\u3001\u6B65\u9AA4\u548C\u8FB9\u754C\u2026": "Write the instructions, steps, and boundaries...", + "\u521B\u5EFA\u548C\u5BFC\u5165\u5747\u4FDD\u5B58\u5230\u5168\u5C40 DSH\uFF0C\u8DE8\u9879\u76EE\u53EF\u7528\u3002": "Created and imported skills are saved to global DSH and available across projects.", + // General Settings & UI "\u901A\u7528\u8BBE\u7F6E": "General Settings", "\u6A21\u578B\u8BBE\u7F6E": "Model Settings", "\u5916\u89C2\u8BBE\u7F6E": "Appearance", + "\u8BED\u8A00\u8BBE\u7F6E": "Language", "\u5FEB\u6377\u952E": "Shortcuts", + "\u5173\u4E8E": "About", + "\u7248\u672C\u53F7": "Version", + "\u68C0\u67E5\u65B0\u7248\u672C": "Check for updates", "\u8BED\u8A00": "Language", "\u7B80\u4F53\u4E2D\u6587": "Simplified Chinese", "\u82F1\u6587": "English", - // Common Notification & Messages - "\u64CD\u4F5C\u6210\u529F": "Operation successful", - "\u64CD\u4F5C\u5931\u8D25": "Operation failed", - "\u66F4\u65B0\u6210\u529F": "Updated successfully", - "\u5220\u9664\u6210\u529F": "Deleted successfully", - "\u521B\u5EFA\u6210\u529F": "Created successfully", - "\u5BFC\u5165\u6210\u529F": "Imported successfully", - "\u5907\u4EFD\u5DF2\u4FDD\u5B58": "Backup saved", - "\u6B63\u5728\u52A0\u8F7D": "Loading" + "\u6DF1\u8272\u6A21\u5F0F": "Dark Mode", + "\u6D45\u8272\u6A21\u5F0F": "Light Mode", + "\u8DDF\u968F\u7CFB\u7EDF": "Follow System", + "\u9000\u51FA": "Exit", + "\u91CD\u542F": "Restart", + // Model & Chat Concepts + "\u6A21\u578B": "Models", + "\u4F1A\u8BDD": "Sessions", + "\u65B0\u4F1A\u8BDD": "New Session", + "\u5386\u53F2\u4F1A\u8BDD": "History", + "\u5DE5\u4F5C\u533A": "Workspace", + "\u6682\u65E0\u4F1A\u8BDD": "No sessions", + "\u53D1\u9001": "Send", + "\u505C\u6B62\u751F\u6210": "Stop generating", + "\u91CD\u65B0\u751F\u6210": "Regenerate", + "\u8017\u65F6": "Duration", + "\u6B65\u9AA4": "Steps", + "\u8F6E\u6B21": "Turns" }; -var PHRASE_PATTERNS = [ +var SORTED_PAIRS = Object.entries(DICTIONARY).sort( + (a, b) => b[0].length - a[0].length +); +var DYNAMIC_PATTERNS = [ [/(\d+)\s*个技能/g, "$1 skills"], [/(\d+)\s*个已启用/g, "$1 enabled"], [/(\d+)\s*个已停用/g, "$1 disabled"], [/(\d+)\s*个被覆盖/g, "$1 shadowed"], + [/(\d+)\s*个诊断异常/g, "$1 issues"], [/(\d+)\s*个文件/g, "$1 files"], [/(\d+)\s*个待处理技能/g, "$1 pending skills"], - [/(\d+)\s*个诊断异常/g, "$1 diagnostic issues"], - [/正在检查更新[…\.]*/g, "Checking for updates..."], - [/正在读取说明[…\.]*/g, "Loading documentation..."], - [/暂无可用技能/g, "No skills available"], - [/暂无数据/g, "No data available"], - [/暂无更新/g, "No updates available"], - [/无法恢复/g, "cannot be restored"], - [/已被覆盖/g, "is shadowed"], - [/移入回收站/g, "Move to Trash"], - [/永久删除/g, "Permanently Delete"] + [/来源:/g, "Source: "], + [/创建于\s*([0-9:\-\/ ]+)/g, "Created at $1"], + [/更新于\s*([0-9:\-\/ ]+)/g, "Updated at $1"], + [/删除于\s*([0-9:\-\/ ]+)/g, "Deleted at $1"], + [/被\s*(.+?)\s*覆盖/g, "Shadowed by $1"] ]; // src/client/index.ts var name = "dsh-plugin-english-client"; -var inject = ["locale", "slots"]; +var inject = []; var CHINESE_REGEX = /[\u4e00-\u9fa5]/; function translateText(text) { if (!text || !CHINESE_REGEX.test(text)) return text; @@ -171,100 +253,138 @@ function translateText(text) { return text.replace(trimmed, DICTIONARY[trimmed]); } let result = text; - for (const [pattern, replacement] of PHRASE_PATTERNS) { + for (const [pattern, replacement] of DYNAMIC_PATTERNS) { result = result.replace(pattern, replacement); } - for (const [cn, en] of Object.entries(DICTIONARY)) { + for (const [cn, en] of SORTED_PAIRS) { if (result.includes(cn)) { result = result.replaceAll(cn, en); + if (!CHINESE_REGEX.test(result)) break; } } return result; } -function translateNode(node) { - if (node.nodeType === Node.TEXT_NODE) { - const val = node.nodeValue; +var isTranslating = false; +function translateElement(root) { + if (!root) return; + if (root.nodeType === Node.TEXT_NODE) { + const val = root.nodeValue; if (val && CHINESE_REGEX.test(val)) { const translated = translateText(val); if (translated !== val) { - node.nodeValue = translated; + root.nodeValue = translated; } } return; } - if (node.nodeType === Node.ELEMENT_NODE) { - const el = node; - const tagName = el.tagName; - if (tagName === "SCRIPT" || tagName === "STYLE" || tagName === "PRE" || tagName === "CODE") { - return; + if (root.nodeType !== Node.ELEMENT_NODE) return; + const el = root; + const tag = el.tagName; + if (tag === "SCRIPT" || tag === "STYLE" || tag === "PRE" || tag === "CODE") return; + for (const attr of ["placeholder", "title", "aria-label"]) { + const val = el.getAttribute(attr); + if (val && CHINESE_REGEX.test(val)) { + el.setAttribute(attr, translateText(val)); } - for (const attr of ["title", "placeholder", "aria-label", "alt"]) { - const val = el.getAttribute(attr); - if (val && CHINESE_REGEX.test(val)) { - el.setAttribute(attr, translateText(val)); + } + const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT, null); + let textNode = walker.nextNode(); + while (textNode) { + const val = textNode.nodeValue; + if (val && CHINESE_REGEX.test(val)) { + const parentTag = textNode.parentElement?.tagName; + if (parentTag !== "SCRIPT" && parentTag !== "STYLE" && parentTag !== "PRE" && parentTag !== "CODE") { + const translated = translateText(val); + if (translated !== val) { + textNode.nodeValue = translated; + } } } - let child = el.firstChild; - while (child) { - translateNode(child); - child = child.nextSibling; - } + textNode = walker.nextNode(); } } -function apply(ctx) { - if (ctx.locale && typeof ctx.locale.register === "function") { - ctx.effect(() => { - if (typeof ctx.locale.subscribe === "function") { - ctx.locale.subscribe(() => { - if (typeof document !== "undefined") { - translateNode(document.body); - } - }); - } - }, "dsh-plugin-english: locale observer"); - } - if (typeof document !== "undefined") { - let pending = false; - const queue = /* @__PURE__ */ new Set(); - const flushTranslations = () => { - pending = false; +function initTranslator() { + if (typeof document === "undefined" || window.__DSH_TRANSLATOR_ACTIVE__) return; + window.__DSH_TRANSLATOR_ACTIVE__ = true; + console.log("[dsh-plugin-english] Universal English translator activated."); + const queue = /* @__PURE__ */ new Set(); + let frameRequested = false; + const processQueue = () => { + frameRequested = false; + isTranslating = true; + try { const nodes = Array.from(queue); queue.clear(); for (const n of nodes) { if (n.isConnected) { - translateNode(n); + translateElement(n); } } - }; - const observer = new MutationObserver((mutations) => { - for (const m of mutations) { - if (m.type === "childList") { - for (let i = 0; i < m.addedNodes.length; i++) { - const n = m.addedNodes[i]; - if (n.nodeType === Node.TEXT_NODE || n.nodeType === Node.ELEMENT_NODE) { - queue.add(n); - } + } finally { + isTranslating = false; + } + }; + const observer = new MutationObserver((mutations) => { + if (isTranslating) return; + for (const m of mutations) { + if (m.type === "childList") { + for (let i = 0; i < m.addedNodes.length; i++) { + const n = m.addedNodes[i]; + if (n.nodeType === Node.TEXT_NODE || n.nodeType === Node.ELEMENT_NODE) { + queue.add(n); } - } else if (m.type === "characterData") { - queue.add(m.target); - } else if (m.type === "attributes") { + } + } else if (m.type === "characterData") { + if (m.target.nodeValue && CHINESE_REGEX.test(m.target.nodeValue)) { queue.add(m.target); } + } else if (m.type === "attributes") { + queue.add(m.target); } - if (!pending && queue.size > 0) { - pending = true; - window.requestAnimationFrame(flushTranslations); - } - }); - translateNode(document.body); - observer.observe(document.body, { + } + if (!frameRequested && queue.size > 0) { + frameRequested = true; + requestAnimationFrame(processQueue); + } + }); + const root = document.documentElement || document.body; + if (root) { + translateElement(root); + observer.observe(root, { childList: true, subtree: true, characterData: true, attributes: true, - attributeFilter: ["title", "placeholder", "aria-label", "alt"] + attributeFilter: ["placeholder", "title", "aria-label"] + }); + } else { + document.addEventListener("DOMContentLoaded", () => { + translateElement(document.body); + observer.observe(document.body, { + childList: true, + subtree: true, + characterData: true, + attributes: true, + attributeFilter: ["placeholder", "title", "aria-label"] + }); }); } + setInterval(() => { + if (document.body && !isTranslating) { + isTranslating = true; + try { + translateElement(document.body); + } finally { + isTranslating = false; + } + } + }, 1e3); +} +if (typeof window !== "undefined") { + initTranslator(); +} +function apply(ctx) { + initTranslator(); } return module.exports; } }); //# sourceMappingURL=client.js.map diff --git a/lib/client.js.map b/lib/client.js.map index 9482d97..c4e508d 100644 --- a/lib/client.js.map +++ b/lib/client.js.map @@ -1,7 +1,7 @@ { "version": 3, "sources": ["../src/client/index.ts", "../src/client/dictionary.ts"], - "sourcesContent": ["import { DICTIONARY, PHRASE_PATTERNS } from \"./dictionary.js\";\n\nexport const name = \"dsh-plugin-english-client\";\nexport const inject = [\"locale\", \"slots\"];\n\nconst CHINESE_REGEX = /[\\u4e00-\\u9fa5]/;\n\n/**\n * Translates a single text string using dictionary and pattern matching.\n */\nfunction translateText(text: string): string {\n if (!text || !CHINESE_REGEX.test(text)) return text;\n\n const trimmed = text.trim();\n if (DICTIONARY[trimmed]) {\n return text.replace(trimmed, DICTIONARY[trimmed]);\n }\n\n let result = text;\n // Apply regex patterns\n for (const [pattern, replacement] of PHRASE_PATTERNS) {\n result = result.replace(pattern, replacement);\n }\n\n // Exact phrase search for substrings\n for (const [cn, en] of Object.entries(DICTIONARY)) {\n if (result.includes(cn)) {\n result = result.replaceAll(cn, en);\n }\n }\n\n return result;\n}\n\n/**\n * Recursively translates text nodes inside a DOM element.\n */\nfunction translateNode(node: Node) {\n if (node.nodeType === Node.TEXT_NODE) {\n const val = node.nodeValue;\n if (val && CHINESE_REGEX.test(val)) {\n const translated = translateText(val);\n if (translated !== val) {\n node.nodeValue = translated;\n }\n }\n return;\n }\n\n if (node.nodeType === Node.ELEMENT_NODE) {\n const el = node as HTMLElement;\n const tagName = el.tagName;\n // Don't translate code blocks or scripts\n if (tagName === \"SCRIPT\" || tagName === \"STYLE\" || tagName === \"PRE\" || tagName === \"CODE\") {\n return;\n }\n\n // Translate common attributes\n for (const attr of [\"title\", \"placeholder\", \"aria-label\", \"alt\"]) {\n const val = el.getAttribute(attr);\n if (val && CHINESE_REGEX.test(val)) {\n el.setAttribute(attr, translateText(val));\n }\n }\n\n // Translate child nodes\n let child = el.firstChild;\n while (child) {\n translateNode(child);\n child = child.nextSibling;\n }\n }\n}\n\nexport function apply(ctx: any) {\n // 1. Hook DSH ctx.locale to inject English fallback into any registered namespace\n if (ctx.locale && typeof ctx.locale.register === \"function\") {\n ctx.effect(() => {\n // Whenever locale state changes or plugins register dictionaries,\n // hook locale to return English translations for known Chinese keys\n if (typeof ctx.locale.subscribe === \"function\") {\n ctx.locale.subscribe(() => {\n if (typeof document !== \"undefined\") {\n translateNode(document.body);\n }\n });\n }\n }, \"dsh-plugin-english: locale observer\");\n }\n\n // 2. High-performance DOM MutationObserver for real-time translation\n if (typeof document !== \"undefined\") {\n let pending = false;\n const queue = new Set();\n\n const flushTranslations = () => {\n pending = false;\n const nodes = Array.from(queue);\n queue.clear();\n for (const n of nodes) {\n if (n.isConnected) {\n translateNode(n);\n }\n }\n };\n\n const observer = new MutationObserver((mutations) => {\n for (const m of mutations) {\n if (m.type === \"childList\") {\n for (let i = 0; i < m.addedNodes.length; i++) {\n const n = m.addedNodes[i];\n if (n.nodeType === Node.TEXT_NODE || n.nodeType === Node.ELEMENT_NODE) {\n queue.add(n);\n }\n }\n } else if (m.type === \"characterData\") {\n queue.add(m.target);\n } else if (m.type === \"attributes\") {\n queue.add(m.target);\n }\n }\n\n if (!pending && queue.size > 0) {\n pending = true;\n window.requestAnimationFrame(flushTranslations);\n }\n });\n\n // Initial translation pass on document body\n translateNode(document.body);\n\n // Observe document for dynamically rendered modals, dialogs, and components\n observer.observe(document.body, {\n childList: true,\n subtree: true,\n characterData: true,\n attributes: true,\n attributeFilter: [\"title\", \"placeholder\", \"aria-label\", \"alt\"],\n });\n }\n}\n", "/**\n * Comprehensive Chinese-to-English translation mapping for DSH plugins\n */\nexport const DICTIONARY: Record = {\n // Navigation & Core Concepts\n \"\u6280\u80FD\u7BA1\u7406\": \"Skills Manager\",\n \"\u6280\u80FD\u7BA1\u7406\u5668\": \"Skills Manager\",\n \"\u6280\u80FD\u5217\u8868\": \"Skills List\",\n \"\u5DF2\u5B89\u88C5\u6280\u80FD\": \"Installed Skills\",\n \"\u53EF\u7528\u6280\u80FD\": \"Available Skills\",\n \"\u53D1\u73B0\u6280\u80FD\": \"Discover Skills\",\n \"\u4ED3\u5E93\u8BA2\u9605\": \"Repository Subscriptions\",\n \"\u4ED3\u5E93\u7BA1\u7406\": \"Repositories\",\n \"\u6280\u80FD\u6765\u6E90\": \"Skill Sources\",\n \"\u5168\u5C40\u6280\u80FD\": \"Global Skills\",\n \"\u9879\u76EE\u6280\u80FD\": \"Project Skills\",\n \"\u4F1A\u8BDD\u6280\u80FD\": \"Session Skills\",\n \"\u56DE\u6536\u7AD9\": \"Trash\",\n \"\u8BCA\u65AD\u5F02\u5E38\": \"Diagnostics\",\n \"\u5F02\u5E38\u8BCA\u65AD\": \"Diagnostics\",\n\n // Actions & Buttons\n \"\u68C0\u67E5\u66F4\u65B0\": \"Check Updates\",\n \"\u6B63\u5728\u68C0\u67E5\u66F4\u65B0\u2026\": \"Checking for updates...\",\n \"\u6B63\u5728\u68C0\u67E5\u66F4\u65B0...\": \"Checking for updates...\",\n \"\u68C0\u67E5\u5E76\u66F4\u65B0\": \"Check & Update\",\n \"\u66F4\u65B0\": \"Update\",\n \"\u5DF2\u662F\u6700\u65B0\": \"Up to date\",\n \"\u53EF\u66F4\u65B0\": \"Update Available\",\n \"\u7ACB\u5373\u66F4\u65B0\": \"Update Now\",\n \"\u786E\u8BA4\u66F4\u65B0\": \"Confirm Update\",\n \"\u56DE\u6EDA\": \"Rollback\",\n \"\u786E\u8BA4\u56DE\u6EDA\": \"Confirm Rollback\",\n \"\u6062\u590D\": \"Restore\",\n \"\u5220\u9664\": \"Delete\",\n \"\u6C38\u4E45\u5220\u9664\": \"Permanently Delete\",\n \"\u6E05\u7A7A\u56DE\u6536\u7AD9\": \"Empty Trash\",\n \"\u79FB\u5165\u56DE\u6536\u7AD9\": \"Move to Trash\",\n \"\u5BFC\u5165\": \"Import\",\n \"\u5BFC\u5165\u6280\u80FD\": \"Import Skill\",\n \"\u5BFC\u51FA\": \"Export\",\n \"\u5BFC\u51FA\u6280\u80FD\": \"Export Skill\",\n \"\u65B0\u5EFA\u6280\u80FD\": \"New Skill\",\n \"\u521B\u5EFA\u6280\u80FD\": \"Create Skill\",\n \"\u5237\u65B0\": \"Refresh\",\n \"\u542F\u7528\": \"Enable\",\n \"\u505C\u7528\": \"Disable\",\n \"\u5DF2\u542F\u7528\": \"Enabled\",\n \"\u5DF2\u505C\u7528\": \"Disabled\",\n \"\u5168\u90E8\u542F\u7528\": \"Enable All\",\n \"\u5168\u90E8\u505C\u7528\": \"Disable All\",\n \"\u5B89\u88C5\": \"Install\",\n \"\u5DF2\u5B89\u88C5\": \"Installed\",\n \"\u5378\u8F7D\": \"Uninstall\",\n \"\u67E5\u770B\": \"View\",\n \"\u67E5\u770B\u8BE6\u60C5\": \"View Details\",\n \"\u67E5\u770B\u8BF4\u660E\": \"View Docs\",\n \"\u4FDD\u5B58\": \"Save\",\n \"\u53D6\u6D88\": \"Cancel\",\n \"\u5173\u95ED\": \"Close\",\n \"\u786E\u5B9A\": \"OK\",\n \"\u590D\u5236\": \"Copy\",\n \"\u590D\u5236\u6210\u529F\": \"Copied\",\n \"\u641C\u7D22\": \"Search\",\n \"\u91CD\u8BD5\": \"Retry\",\n\n // Status & Counts\n \"\u5168\u90E8\": \"All\",\n \"\u72B6\u6001\": \"Status\",\n \"\u7C7B\u578B\": \"Type\",\n \"\u540D\u79F0\": \"Name\",\n \"\u7248\u672C\": \"Version\",\n \"\u64CD\u4F5C\": \"Actions\",\n \"\u6765\u6E90\": \"Source\",\n \"\u8DEF\u5F84\": \"Path\",\n \"\u63CF\u8FF0\": \"Description\",\n \"\u8BE6\u60C5\": \"Details\",\n \"\u5927\u5C0F\": \"Size\",\n \"\u66F4\u65B0\u65F6\u95F4\": \"Updated\",\n \"\u521B\u5EFA\u65F6\u95F4\": \"Created\",\n \"\u4E2A\u6280\u80FD\": \" skills\",\n \"\u4E2A\u5DF2\u542F\u7528\": \" enabled\",\n \"\u4E2A\u5DF2\u505C\u7528\": \" disabled\",\n \"\u4E2A\u88AB\u8986\u76D6\": \" shadowed\",\n \"\u4E2A\u5F85\u5904\u7406\u6280\u80FD\": \" pending skills\",\n \"\u4E2A\u8BCA\u65AD\u5F02\u5E38\": \" diagnostic issues\",\n\n // Form Fields & Hints\n \"\u6280\u80FD\u540D\u79F0\": \"Skill Name\",\n \"\u6280\u80FD\u63CF\u8FF0\": \"Description\",\n \"\u6280\u80FD\u5185\u5BB9\": \"Skill Body\",\n \"\u6280\u80FD\u6B63\u6587\": \"Skill Body\",\n \"\u4ED3\u5E93\u5730\u5740\": \"Repository URL\",\n \"\u641C\u7D22\u6280\u80FD\": \"Search skills...\",\n \"\u641C\u7D22\u6280\u80FD\u540D\u79F0\u3001\u63CF\u8FF0\u6216\u6765\u6E90\": \"Search skills by name, description, or source...\",\n \"\u8BF7\u8F93\u5165\u4ED3\u5E93\u5730\u5740\": \"Enter repository URL...\",\n \"\u6CA1\u6709\u627E\u5230\u5339\u914D\u7684\u6280\u80FD\": \"No matching skills found\",\n \"\u6682\u65E0\u6280\u80FD\": \"No skills available\",\n \"\u56DE\u6536\u7AD9\u4E3A\u7A7A\": \"Trash is empty\",\n \"\u52A0\u8F7D\u4E2D\u2026\": \"Loading...\",\n \"\u52A0\u8F7D\u4E2D...\": \"Loading...\",\n \"\u6B63\u5728\u8BFB\u53D6\u8BF4\u660E\u2026\": \"Loading documentation...\",\n\n // Settings & Configuration\n \"\u901A\u7528\u8BBE\u7F6E\": \"General Settings\",\n \"\u6A21\u578B\u8BBE\u7F6E\": \"Model Settings\",\n \"\u5916\u89C2\u8BBE\u7F6E\": \"Appearance\",\n \"\u5FEB\u6377\u952E\": \"Shortcuts\",\n \"\u8BED\u8A00\": \"Language\",\n \"\u7B80\u4F53\u4E2D\u6587\": \"Simplified Chinese\",\n \"\u82F1\u6587\": \"English\",\n\n // Common Notification & Messages\n \"\u64CD\u4F5C\u6210\u529F\": \"Operation successful\",\n \"\u64CD\u4F5C\u5931\u8D25\": \"Operation failed\",\n \"\u66F4\u65B0\u6210\u529F\": \"Updated successfully\",\n \"\u5220\u9664\u6210\u529F\": \"Deleted successfully\",\n \"\u521B\u5EFA\u6210\u529F\": \"Created successfully\",\n \"\u5BFC\u5165\u6210\u529F\": \"Imported successfully\",\n \"\u5907\u4EFD\u5DF2\u4FDD\u5B58\": \"Backup saved\",\n \"\u6B63\u5728\u52A0\u8F7D\": \"Loading\",\n};\n\n// Regex phrase replacements for dynamic strings (e.g. \"3 \u4E2A\u6280\u80FD\")\nexport const PHRASE_PATTERNS: Array<[RegExp, string]> = [\n [/(\\d+)\\s*\u4E2A\u6280\u80FD/g, \"$1 skills\"],\n [/(\\d+)\\s*\u4E2A\u5DF2\u542F\u7528/g, \"$1 enabled\"],\n [/(\\d+)\\s*\u4E2A\u5DF2\u505C\u7528/g, \"$1 disabled\"],\n [/(\\d+)\\s*\u4E2A\u88AB\u8986\u76D6/g, \"$1 shadowed\"],\n [/(\\d+)\\s*\u4E2A\u6587\u4EF6/g, \"$1 files\"],\n [/(\\d+)\\s*\u4E2A\u5F85\u5904\u7406\u6280\u80FD/g, \"$1 pending skills\"],\n [/(\\d+)\\s*\u4E2A\u8BCA\u65AD\u5F02\u5E38/g, \"$1 diagnostic issues\"],\n [/\u6B63\u5728\u68C0\u67E5\u66F4\u65B0[\u2026\\.]*/g, \"Checking for updates...\"],\n [/\u6B63\u5728\u8BFB\u53D6\u8BF4\u660E[\u2026\\.]*/g, \"Loading documentation...\"],\n [/\u6682\u65E0\u53EF\u7528\u6280\u80FD/g, \"No skills available\"],\n [/\u6682\u65E0\u6570\u636E/g, \"No data available\"],\n [/\u6682\u65E0\u66F4\u65B0/g, \"No updates available\"],\n [/\u65E0\u6CD5\u6062\u590D/g, \"cannot be restored\"],\n [/\u5DF2\u88AB\u8986\u76D6/g, \"is shadowed\"],\n [/\u79FB\u5165\u56DE\u6536\u7AD9/g, \"Move to Trash\"],\n [/\u6C38\u4E45\u5220\u9664/g, \"Permanently Delete\"],\n];\n"], - "mappings": "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", + "sourcesContent": ["import { DICTIONARY, SORTED_PAIRS, DYNAMIC_PATTERNS } from \"./dictionary.js\";\n\nexport const name = \"dsh-plugin-english-client\";\nexport const inject = [];\n\nconst CHINESE_REGEX = /[\\u4e00-\\u9fa5]/;\n\n/**\n * Translates a single text string using dictionary and pattern matching.\n */\nexport function translateText(text: string): string {\n if (!text || !CHINESE_REGEX.test(text)) return text;\n\n const trimmed = text.trim();\n if (DICTIONARY[trimmed]) {\n return text.replace(trimmed, DICTIONARY[trimmed]);\n }\n\n let result = text;\n // Dynamic regex patterns (counts, numbers, time)\n for (const [pattern, replacement] of DYNAMIC_PATTERNS) {\n result = result.replace(pattern, replacement);\n }\n\n // Exact phrase search for substrings (longest phrases first)\n for (const [cn, en] of SORTED_PAIRS) {\n if (result.includes(cn)) {\n result = result.replaceAll(cn, en);\n if (!CHINESE_REGEX.test(result)) break;\n }\n }\n\n return result;\n}\n\nlet isTranslating = false;\n\n/**\n * Translates an element and all its children using native TreeWalker.\n */\nexport function translateElement(root: Node) {\n if (!root) return;\n\n if (root.nodeType === Node.TEXT_NODE) {\n const val = root.nodeValue;\n if (val && CHINESE_REGEX.test(val)) {\n const translated = translateText(val);\n if (translated !== val) {\n root.nodeValue = translated;\n }\n }\n return;\n }\n\n if (root.nodeType !== Node.ELEMENT_NODE) return;\n const el = root as HTMLElement;\n\n const tag = el.tagName;\n if (tag === \"SCRIPT\" || tag === \"STYLE\" || tag === \"PRE\" || tag === \"CODE\") return;\n\n // Translate attributes\n for (const attr of [\"placeholder\", \"title\", \"aria-label\"]) {\n const val = el.getAttribute(attr);\n if (val && CHINESE_REGEX.test(val)) {\n el.setAttribute(attr, translateText(val));\n }\n }\n\n // Fast native TreeWalker\n const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT, null);\n let textNode = walker.nextNode();\n while (textNode) {\n const val = textNode.nodeValue;\n if (val && CHINESE_REGEX.test(val)) {\n const parentTag = textNode.parentElement?.tagName;\n if (parentTag !== \"SCRIPT\" && parentTag !== \"STYLE\" && parentTag !== \"PRE\" && parentTag !== \"CODE\") {\n const translated = translateText(val);\n if (translated !== val) {\n textNode.nodeValue = translated;\n }\n }\n }\n textNode = walker.nextNode();\n }\n}\n\nfunction initTranslator() {\n if (typeof document === \"undefined\" || (window as any).__DSH_TRANSLATOR_ACTIVE__) return;\n (window as any).__DSH_TRANSLATOR_ACTIVE__ = true;\n\n console.log(\"[dsh-plugin-english] Universal English translator activated.\");\n\n const queue = new Set();\n let frameRequested = false;\n\n const processQueue = () => {\n frameRequested = false;\n isTranslating = true;\n try {\n const nodes = Array.from(queue);\n queue.clear();\n for (const n of nodes) {\n if (n.isConnected) {\n translateElement(n);\n }\n }\n } finally {\n isTranslating = false;\n }\n };\n\n const observer = new MutationObserver((mutations) => {\n if (isTranslating) return;\n\n for (const m of mutations) {\n if (m.type === \"childList\") {\n for (let i = 0; i < m.addedNodes.length; i++) {\n const n = m.addedNodes[i];\n if (n.nodeType === Node.TEXT_NODE || n.nodeType === Node.ELEMENT_NODE) {\n queue.add(n);\n }\n }\n } else if (m.type === \"characterData\") {\n if (m.target.nodeValue && CHINESE_REGEX.test(m.target.nodeValue)) {\n queue.add(m.target);\n }\n } else if (m.type === \"attributes\") {\n queue.add(m.target);\n }\n }\n\n if (!frameRequested && queue.size > 0) {\n frameRequested = true;\n requestAnimationFrame(processQueue);\n }\n });\n\n // Observe root\n const root = document.documentElement || document.body;\n if (root) {\n translateElement(root);\n observer.observe(root, {\n childList: true,\n subtree: true,\n characterData: true,\n attributes: true,\n attributeFilter: [\"placeholder\", \"title\", \"aria-label\"],\n });\n } else {\n document.addEventListener(\"DOMContentLoaded\", () => {\n translateElement(document.body);\n observer.observe(document.body, {\n childList: true,\n subtree: true,\n characterData: true,\n attributes: true,\n attributeFilter: [\"placeholder\", \"title\", \"aria-label\"],\n });\n });\n }\n\n // Periodic safety check every 1s for any dynamically inserted lazy elements\n setInterval(() => {\n if (document.body && !isTranslating) {\n isTranslating = true;\n try {\n translateElement(document.body);\n } finally {\n isTranslating = false;\n }\n }\n }, 1000);\n}\n\n// Start immediately when the bundle loads in browser\nif (typeof window !== \"undefined\") {\n initTranslator();\n}\n\nexport function apply(ctx: any) {\n initTranslator();\n}\n", "/**\n * Comprehensive Chinese-to-English translation dictionary for DeepSeek Harness plugins\n */\nexport const DICTIONARY: Record = {\n // DSH Marketplace & Store\n \"\u63D2\u4EF6\u5E02\u573A\": \"Plugin Marketplace\",\n \"\u901B\u4E00\u901B\uFF0C\u70B9\u4E00\u4E0B\uFF0C\u88C5\u597D\u3002\": \"Browse, click, and install.\",\n \"\u5168\u90E8\u63D2\u4EF6\": \"All Plugins\",\n \"\u5DF2\u5B89\u88C5\u63D2\u4EF6\": \"Installed Plugins\",\n \"\u63D2\u4EF6\": \"Plugins\",\n \"\u5E02\u573A\": \"Marketplace\",\n \"\u70ED\u95E8\u63D2\u4EF6\": \"Popular Plugins\",\n \"\u6700\u65B0\u4E0A\u67B6\": \"Recently Added\",\n \"\u63A8\u8350\u63D2\u4EF6\": \"Recommended Plugins\",\n \"\u4E0B\u8F7D\u91CF\": \"Downloads\",\n \"\u8BC4\u5206\": \"Rating\",\n \"\u4F5C\u8005\": \"Author\",\n \"\u5B89\u88C5\u91CF\": \"Installs\",\n \"\u5B89\u88C5\u63D2\u4EF6\": \"Install Plugin\",\n \"\u5378\u8F7D\u63D2\u4EF6\": \"Uninstall Plugin\",\n \"\u66F4\u65B0\u63D2\u4EF6\": \"Update Plugin\",\n \"\u7B2C\u4E09\u65B9\u63D2\u4EF6\": \"Third-Party Plugins\",\n \"\u5B98\u65B9\u63D2\u4EF6\": \"Official Plugins\",\n \"\u641C\u7D22\u63D2\u4EF6\": \"Search plugins...\",\n \"\u641C\u7D22\u63D2\u4EF6\u540D\u79F0\u6216\u4F5C\u8005\": \"Search by plugin name or author...\",\n \"\u4E00\u952E\u5B89\u88C5\": \"Install\",\n \"\u4E00\u952E\u66F4\u65B0\": \"Update\",\n \"\u6B63\u5728\u4E0B\u8F7D\": \"Downloading...\",\n \"\u6B63\u5728\u5B89\u88C5\": \"Installing...\",\n \"\u5B89\u88C5\u5B8C\u6210\": \"Installed\",\n \"\u5378\u8F7D\u5B8C\u6210\": \"Uninstalled\",\n\n // Skills Manager Navigation & Tabs\n \"\u6280\u80FD\u7BA1\u7406\": \"Skills Manager\",\n \"\u6280\u80FD\u7BA1\u7406\u5668\": \"Skills Manager\",\n \"\u7EDF\u4E00\u52A0\u8F7D\u548C\u7BA1\u7406\u672C\u673A Agent Skills\u3002\": \"Load and manage Agent Skills on this machine in one place.\",\n \"\u6280\u80FD\u5217\u8868\": \"Skills List\",\n \"\u5DF2\u5B89\u88C5\u6280\u80FD\": \"Installed Skills\",\n \"\u53EF\u7528\u6280\u80FD\": \"Available Skills\",\n \"\u53D1\u73B0\u6280\u80FD\": \"Discover Skills\",\n \"\u4ED3\u5E93\u8BA2\u9605\": \"Repositories\",\n \"\u4ED3\u5E93\u7BA1\u7406\": \"Repositories\",\n \"\u6280\u80FD\u6765\u6E90\": \"Skill Sources\",\n \"\u5168\u5C40\u6280\u80FD\": \"Global Skills\",\n \"\u9879\u76EE\u6280\u80FD\": \"Project Skills\",\n \"\u516C\u5171 Agent\": \"Shared Agent\",\n \"\u9879\u76EE Agent\": \"Project Agent\",\n \"\u9879\u76EE Skills\": \"Project Skills\",\n \"\u4F1A\u8BDD\u6280\u80FD\": \"Session Skills\",\n \"\u56DE\u6536\u7AD9\": \"Trash\",\n \"\u8BCA\u65AD\u5F02\u5E38\": \"Diagnostics\",\n \"\u5F02\u5E38\u8BCA\u65AD\": \"Diagnostics\",\n \"\u6E90\u6587\u4EF6\u53EA\u8BFB\": \"Read-only\",\n \"\u53EA\u8BFB\": \"Read-only\",\n \"\u521B\u5EFA\u4F4D\u7F6E\": \"Location\",\n \"\u5168\u5C40 DSH\": \"Global DSH\",\n \"\u9879\u76EE DSH\": \"Project DSH\",\n \"DSH \u6280\u80FD\": \"DSH Skills\",\n\n // Actions & Buttons\n \"\u521B\u5EFA\u6280\u80FD\": \"Create Skill\",\n \"\u521B\u5EFA\u65B0\u6280\u80FD\": \"Create Skill\",\n \"\u65B0\u5EFA\u6280\u80FD\": \"New Skill\",\n \"\u7ACB\u5373\u521B\u5EFA\": \"Create Now\",\n \"\u5BFC\u5165\u6280\u80FD\": \"Import Skill\",\n \"\u5BFC\u51FA\u6280\u80FD\": \"Export Skill\",\n \"\u5BFC\u5165\": \"Import\",\n \"\u5BFC\u51FA\": \"Export\",\n \"\u5237\u65B0\": \"Refresh\",\n \"\u68C0\u67E5\u66F4\u65B0\": \"Check Updates\",\n \"\u6B63\u5728\u68C0\u67E5\u66F4\u65B0\u2026\": \"Checking for updates...\",\n \"\u6B63\u5728\u68C0\u67E5\u66F4\u65B0...\": \"Checking for updates...\",\n \"\u68C0\u67E5\u5E76\u66F4\u65B0\": \"Check & Update\",\n \"\u66F4\u65B0\": \"Update\",\n \"\u7BA1\u7406\u66F4\u65B0\": \"Manage Updates\",\n \"\u7ACB\u5373\u66F4\u65B0\": \"Update Now\",\n \"\u786E\u8BA4\u66F4\u65B0\": \"Confirm Update\",\n \"\u56DE\u6EDA\": \"Rollback\",\n \"\u786E\u8BA4\u56DE\u6EDA\": \"Confirm Rollback\",\n \"\u6062\u590D\": \"Restore\",\n \"\u79FB\u5230\u56DE\u6536\u7AD9\": \"Move to Trash\",\n \"\u79FB\u5165\u56DE\u6536\u7AD9\": \"Move to Trash\",\n \"\u6C38\u4E45\u5220\u9664\": \"Delete Forever\",\n \"\u6E05\u7A7A\u56DE\u6536\u7AD9\": \"Empty Trash\",\n \"\u5220\u9664\": \"Delete\",\n \"\u542F\u7528\": \"Enable\",\n \"\u505C\u7528\": \"Disable\",\n \"\u5DF2\u542F\u7528\": \"Enabled\",\n \"\u5DF2\u505C\u7528\": \"Disabled\",\n \"\u5168\u90E8\u542F\u7528\": \"Enable All\",\n \"\u5168\u90E8\u505C\u7528\": \"Disable All\",\n \"\u542F\u505C\u6765\u6E90\": \"Toggle Source\",\n \"\u542F\u505C\u6280\u80FD\": \"Toggle Skill\",\n \"\u5B89\u88C5\": \"Install\",\n \"\u5DF2\u5B89\u88C5\": \"Installed\",\n \"\u5378\u8F7D\": \"Uninstall\",\n \"\u67E5\u770B\u8BE6\u60C5\": \"View Details\",\n \"\u67E5\u770B\u8BF4\u660E\": \"View Docs\",\n \"\u67E5\u770B\": \"View\",\n \"\u66F4\u591A\": \"More\",\n \"\u53D6\u6D88\": \"Cancel\",\n \"\u4FDD\u5B58\": \"Save\",\n \"\u5173\u95ED\": \"Close\",\n \"\u786E\u5B9A\": \"OK\",\n \"\u590D\u5236\": \"Copy\",\n \"\u5DF2\u590D\u5236\": \"Copied\",\n \"\u590D\u5236\u6210\u529F\": \"Copied\",\n \"\u641C\u7D22\": \"Search\",\n \"\u91CD\u8BD5\": \"Retry\",\n \"\u9009\u62E9\u6587\u4EF6\": \"Choose File\",\n \"\u9009\u62E9\u6587\u4EF6\u5939\": \"Choose Folder\",\n \"\u95EE\u9898\u53CD\u9988\": \"Issues\",\n \"\u8BBE\u7F6E\": \"Settings\",\n\n // Status & Filters\n \"\u5168\u90E8\u6765\u6E90\": \"All Sources\",\n \"\u5168\u90E8\u72B6\u6001\": \"All Statuses\",\n \"\u8C03\u7528\u72B6\u6001\": \"Invocation Status\",\n \"\u72B6\u6001\": \"Status\",\n \"\u6765\u6E90\": \"Source\",\n \"\u88AB\u8986\u76D6\": \"Shadowed\",\n \"\u540D\u79F0\": \"Name\",\n \"\u7248\u672C\": \"Version\",\n \"\u5927\u5C0F\": \"Size\",\n \"\u7C7B\u578B\": \"Type\",\n \"\u64CD\u4F5C\": \"Actions\",\n \"\u8BE6\u60C5\": \"Details\",\n \"\u63CF\u8FF0\": \"Description\",\n \"\u7B80\u4ECB\": \"Description\",\n \"\u6B63\u6587\": \"Body\",\n \"\u6E90\u6587\u4EF6\": \"Source File\",\n \"\u66F4\u65B0\u65F6\u95F4\": \"Updated\",\n \"\u521B\u5EFA\u65F6\u95F4\": \"Created\",\n \"\u5220\u9664\u4E8E\": \"Deleted at\",\n \"\u6765\u6E90\uFF1A\": \"Source: \",\n\n // Empty & Loading States\n \"\u6B63\u5728\u52A0\u8F7D\u6280\u80FD\u2026\": \"Loading skills...\",\n \"\u6B63\u5728\u52A0\u8F7D\u6280\u80FD...\": \"Loading skills...\",\n \"\u6B63\u5728\u52A0\u8F7D\u2026\": \"Loading...\",\n \"\u6B63\u5728\u52A0\u8F7D...\": \"Loading...\",\n \"\u52A0\u8F7D\u4E2D\u2026\": \"Loading...\",\n \"\u52A0\u8F7D\u4E2D...\": \"Loading...\",\n \"\u6B63\u5728\u8BFB\u53D6\u8BF4\u660E\u2026\": \"Loading documentation...\",\n \"\u6B63\u5728\u8BFB\u53D6\u8BF4\u660E...\": \"Loading documentation...\",\n \"\u6CA1\u6709\u5339\u914D\u7684\u6280\u80FD\u3002\": \"No matching skills.\",\n \"\u6CA1\u6709\u5339\u914D\u7684\u6280\u80FD\": \"No matching skills\",\n \"\u8BE5\u6765\u6E90\u76EE\u5F55\u4E0D\u5B58\u5728\u6216\u6682\u65F6\u6CA1\u6709\u6280\u80FD\u3002\": \"This source directory does not exist or has no skills.\",\n \"\u56DE\u6536\u7AD9\u4E3A\u7A7A\u3002\": \"Trash is empty.\",\n \"\u56DE\u6536\u7AD9\u4E3A\u7A7A\": \"Trash is empty\",\n \"\u6682\u65E0\u6280\u80FD\": \"No skills available\",\n \"\u6682\u65E0\u6570\u636E\": \"No data available\",\n \"\u672A\u63D0\u4F9B\u7B80\u4ECB\": \"No description provided\",\n \"\u672A\u53D1\u73B0\u8BCA\u65AD\u95EE\u9898\u3002\": \"No diagnostic issues found.\",\n\n // Upload & Import Modal\n \"\u62D6\u62FD\u6280\u80FD\u5230\u6B64\u5904\": \"Drop skill here\",\n \"\u652F\u6301 .zip\u3001\u6280\u80FD\u6587\u4EF6\u5939\u6216\u5355\u4E2A SKILL.md\": \"Supports .zip, a skill folder, or single SKILL.md\",\n \"\u6587\u4EF6\u8981\u6C42\": \"Requirements\",\n \"\u538B\u7F29\u5305\u6216\u6587\u4EF6\u5939\u9700\u5305\u542B SKILL.md\": \"Archives or folders must contain SKILL.md\",\n \"SKILL.md \u9700\u5305\u542B YAML \u683C\u5F0F\u7684\u6280\u80FD\u540D\u79F0\u548C\u63CF\u8FF0\": \"SKILL.md must include YAML name and description\",\n \"\u5BFC\u5165\u65F6\u590D\u5236\u5B8C\u6574\u5185\u5BB9\uFF0C\u4E0D\u4FEE\u6539\u539F\u59CB\u6765\u6E90\": \"Import copies full content without modifying source\",\n \"\u5BFC\u5165\u5230\u5168\u5C40 DSH\": \"Import to global DSH\",\n \"\u79FB\u9664\u6240\u9009\u5185\u5BB9\": \"Remove selection\",\n \"\u8BF7\u9009\u62E9 .zip \u6216\u5355\u4E2A SKILL.md\u3002\": \"Please choose a .zip or single SKILL.md.\",\n \"\u6240\u9009\u6587\u4EF6\u5939\u4E2D\u6CA1\u6709\u627E\u5230 SKILL.md\u3002\": \"No SKILL.md found in the selected folder.\",\n\n // Creation Form\n \"\u4F8B\u5982 code-review-helper\": \"e.g. code-review-helper\",\n \"\u4E00\u53E5\u8BDD\u8BF4\u660E\u4EC0\u4E48\u65F6\u5019\u4F7F\u7528\": \"One sentence describing when to use it\",\n \"\u6B63\u6587\uFF08Markdown\uFF09\": \"Body (Markdown)\",\n \"\u5199\u4E0B\u6280\u80FD\u8981\u9075\u5FAA\u7684\u6307\u4EE4\u3001\u6B65\u9AA4\u548C\u8FB9\u754C\u2026\": \"Write the instructions, steps, and boundaries...\",\n \"\u521B\u5EFA\u548C\u5BFC\u5165\u5747\u4FDD\u5B58\u5230\u5168\u5C40 DSH\uFF0C\u8DE8\u9879\u76EE\u53EF\u7528\u3002\": \"Created and imported skills are saved to global DSH and available across projects.\",\n\n // General Settings & UI\n \"\u901A\u7528\u8BBE\u7F6E\": \"General Settings\",\n \"\u6A21\u578B\u8BBE\u7F6E\": \"Model Settings\",\n \"\u5916\u89C2\u8BBE\u7F6E\": \"Appearance\",\n \"\u8BED\u8A00\u8BBE\u7F6E\": \"Language\",\n \"\u5FEB\u6377\u952E\": \"Shortcuts\",\n \"\u5173\u4E8E\": \"About\",\n \"\u7248\u672C\u53F7\": \"Version\",\n \"\u68C0\u67E5\u65B0\u7248\u672C\": \"Check for updates\",\n \"\u8BED\u8A00\": \"Language\",\n \"\u7B80\u4F53\u4E2D\u6587\": \"Simplified Chinese\",\n \"\u82F1\u6587\": \"English\",\n \"\u6DF1\u8272\u6A21\u5F0F\": \"Dark Mode\",\n \"\u6D45\u8272\u6A21\u5F0F\": \"Light Mode\",\n \"\u8DDF\u968F\u7CFB\u7EDF\": \"Follow System\",\n \"\u9000\u51FA\": \"Exit\",\n \"\u91CD\u542F\": \"Restart\",\n\n // Model & Chat Concepts\n \"\u6A21\u578B\": \"Models\",\n \"\u4F1A\u8BDD\": \"Sessions\",\n \"\u65B0\u4F1A\u8BDD\": \"New Session\",\n \"\u5386\u53F2\u4F1A\u8BDD\": \"History\",\n \"\u5DE5\u4F5C\u533A\": \"Workspace\",\n \"\u6682\u65E0\u4F1A\u8BDD\": \"No sessions\",\n \"\u53D1\u9001\": \"Send\",\n \"\u505C\u6B62\u751F\u6210\": \"Stop generating\",\n \"\u91CD\u65B0\u751F\u6210\": \"Regenerate\",\n \"\u8017\u65F6\": \"Duration\",\n \"\u6B65\u9AA4\": \"Steps\",\n \"\u8F6E\u6B21\": \"Turns\",\n};\n\n// Sorted array of [chinese, english] pairs, longest Chinese phrases first\n// (ensures compound phrases are matched before single words)\nexport const SORTED_PAIRS: Array<[string, string]> = Object.entries(DICTIONARY).sort(\n (a, b) => b[0].length - a[0].length\n);\n\n// Dynamic pattern replacements\nexport const DYNAMIC_PATTERNS: Array<[RegExp, string]> = [\n [/(\\d+)\\s*\u4E2A\u6280\u80FD/g, \"$1 skills\"],\n [/(\\d+)\\s*\u4E2A\u5DF2\u542F\u7528/g, \"$1 enabled\"],\n [/(\\d+)\\s*\u4E2A\u5DF2\u505C\u7528/g, \"$1 disabled\"],\n [/(\\d+)\\s*\u4E2A\u88AB\u8986\u76D6/g, \"$1 shadowed\"],\n [/(\\d+)\\s*\u4E2A\u8BCA\u65AD\u5F02\u5E38/g, \"$1 issues\"],\n [/(\\d+)\\s*\u4E2A\u6587\u4EF6/g, \"$1 files\"],\n [/(\\d+)\\s*\u4E2A\u5F85\u5904\u7406\u6280\u80FD/g, \"$1 pending skills\"],\n [/\u6765\u6E90\uFF1A/g, \"Source: \"],\n [/\u521B\u5EFA\u4E8E\\s*([0-9:\\-\\/ ]+)/g, \"Created at $1\"],\n [/\u66F4\u65B0\u4E8E\\s*([0-9:\\-\\/ ]+)/g, \"Updated at $1\"],\n [/\u5220\u9664\u4E8E\\s*([0-9:\\-\\/ ]+)/g, \"Deleted at $1\"],\n [/\u88AB\\s*(.+?)\\s*\u8986\u76D6/g, \"Shadowed by $1\"],\n];\n"], + "mappings": "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", "names": [] } diff --git a/package.json b/package.json index 12c6c46..a934fa0 100644 --- a/package.json +++ b/package.json @@ -30,10 +30,7 @@ "patch": "./cordis.patch.yml" }, "client": { - "inject": [ - "@deepseek-ai/dsh-client-ui-slots", - "@deepseek-ai/dsh-client-locale" - ], + "immediately": true, "platform": "web" } }, diff --git a/src/client/dictionary.ts b/src/client/dictionary.ts index 667d36d..9d8c469 100644 --- a/src/client/dictionary.ts +++ b/src/client/dictionary.ts @@ -1,142 +1,228 @@ /** - * Comprehensive Chinese-to-English translation mapping for DSH plugins + * Comprehensive Chinese-to-English translation dictionary for DeepSeek Harness plugins */ export const DICTIONARY: Record = { - // Navigation & Core Concepts + // DSH Marketplace & Store + "插件市场": "Plugin Marketplace", + "逛一逛,点一下,装好。": "Browse, click, and install.", + "全部插件": "All Plugins", + "已安装插件": "Installed Plugins", + "插件": "Plugins", + "市场": "Marketplace", + "热门插件": "Popular Plugins", + "最新上架": "Recently Added", + "推荐插件": "Recommended Plugins", + "下载量": "Downloads", + "评分": "Rating", + "作者": "Author", + "安装量": "Installs", + "安装插件": "Install Plugin", + "卸载插件": "Uninstall Plugin", + "更新插件": "Update Plugin", + "第三方插件": "Third-Party Plugins", + "官方插件": "Official Plugins", + "搜索插件": "Search plugins...", + "搜索插件名称或作者": "Search by plugin name or author...", + "一键安装": "Install", + "一键更新": "Update", + "正在下载": "Downloading...", + "正在安装": "Installing...", + "安装完成": "Installed", + "卸载完成": "Uninstalled", + + // Skills Manager Navigation & Tabs "技能管理": "Skills Manager", "技能管理器": "Skills Manager", + "统一加载和管理本机 Agent Skills。": "Load and manage Agent Skills on this machine in one place.", "技能列表": "Skills List", "已安装技能": "Installed Skills", "可用技能": "Available Skills", "发现技能": "Discover Skills", - "仓库订阅": "Repository Subscriptions", + "仓库订阅": "Repositories", "仓库管理": "Repositories", "技能来源": "Skill Sources", "全局技能": "Global Skills", "项目技能": "Project Skills", + "公共 Agent": "Shared Agent", + "项目 Agent": "Project Agent", + "项目 Skills": "Project Skills", "会话技能": "Session Skills", "回收站": "Trash", "诊断异常": "Diagnostics", "异常诊断": "Diagnostics", + "源文件只读": "Read-only", + "只读": "Read-only", + "创建位置": "Location", + "全局 DSH": "Global DSH", + "项目 DSH": "Project DSH", + "DSH 技能": "DSH Skills", // Actions & Buttons + "创建技能": "Create Skill", + "创建新技能": "Create Skill", + "新建技能": "New Skill", + "立即创建": "Create Now", + "导入技能": "Import Skill", + "导出技能": "Export Skill", + "导入": "Import", + "导出": "Export", + "刷新": "Refresh", "检查更新": "Check Updates", "正在检查更新…": "Checking for updates...", "正在检查更新...": "Checking for updates...", "检查并更新": "Check & Update", "更新": "Update", - "已是最新": "Up to date", - "可更新": "Update Available", + "管理更新": "Manage Updates", "立即更新": "Update Now", "确认更新": "Confirm Update", "回滚": "Rollback", "确认回滚": "Confirm Rollback", "恢复": "Restore", - "删除": "Delete", - "永久删除": "Permanently Delete", - "清空回收站": "Empty Trash", + "移到回收站": "Move to Trash", "移入回收站": "Move to Trash", - "导入": "Import", - "导入技能": "Import Skill", - "导出": "Export", - "导出技能": "Export Skill", - "新建技能": "New Skill", - "创建技能": "Create Skill", - "刷新": "Refresh", + "永久删除": "Delete Forever", + "清空回收站": "Empty Trash", + "删除": "Delete", "启用": "Enable", "停用": "Disable", "已启用": "Enabled", "已停用": "Disabled", "全部启用": "Enable All", "全部停用": "Disable All", + "启停来源": "Toggle Source", + "启停技能": "Toggle Skill", "安装": "Install", "已安装": "Installed", "卸载": "Uninstall", - "查看": "View", "查看详情": "View Details", "查看说明": "View Docs", - "保存": "Save", + "查看": "View", + "更多": "More", "取消": "Cancel", + "保存": "Save", "关闭": "Close", "确定": "OK", "复制": "Copy", + "已复制": "Copied", "复制成功": "Copied", "搜索": "Search", "重试": "Retry", + "选择文件": "Choose File", + "选择文件夹": "Choose Folder", + "问题反馈": "Issues", + "设置": "Settings", - // Status & Counts - "全部": "All", + // Status & Filters + "全部来源": "All Sources", + "全部状态": "All Statuses", + "调用状态": "Invocation Status", "状态": "Status", - "类型": "Type", + "来源": "Source", + "被覆盖": "Shadowed", "名称": "Name", "版本": "Version", - "操作": "Actions", - "来源": "Source", - "路径": "Path", - "描述": "Description", - "详情": "Details", "大小": "Size", + "类型": "Type", + "操作": "Actions", + "详情": "Details", + "描述": "Description", + "简介": "Description", + "正文": "Body", + "源文件": "Source File", "更新时间": "Updated", "创建时间": "Created", - "个技能": " skills", - "个已启用": " enabled", - "个已停用": " disabled", - "个被覆盖": " shadowed", - "个待处理技能": " pending skills", - "个诊断异常": " diagnostic issues", + "删除于": "Deleted at", + "来源:": "Source: ", - // Form Fields & Hints - "技能名称": "Skill Name", - "技能描述": "Description", - "技能内容": "Skill Body", - "技能正文": "Skill Body", - "仓库地址": "Repository URL", - "搜索技能": "Search skills...", - "搜索技能名称、描述或来源": "Search skills by name, description, or source...", - "请输入仓库地址": "Enter repository URL...", - "没有找到匹配的技能": "No matching skills found", - "暂无技能": "No skills available", - "回收站为空": "Trash is empty", + // Empty & Loading States + "正在加载技能…": "Loading skills...", + "正在加载技能...": "Loading skills...", + "正在加载…": "Loading...", + "正在加载...": "Loading...", "加载中…": "Loading...", "加载中...": "Loading...", "正在读取说明…": "Loading documentation...", + "正在读取说明...": "Loading documentation...", + "没有匹配的技能。": "No matching skills.", + "没有匹配的技能": "No matching skills", + "该来源目录不存在或暂时没有技能。": "This source directory does not exist or has no skills.", + "回收站为空。": "Trash is empty.", + "回收站为空": "Trash is empty", + "暂无技能": "No skills available", + "暂无数据": "No data available", + "未提供简介": "No description provided", + "未发现诊断问题。": "No diagnostic issues found.", - // Settings & Configuration + // Upload & Import Modal + "拖拽技能到此处": "Drop skill here", + "支持 .zip、技能文件夹或单个 SKILL.md": "Supports .zip, a skill folder, or single SKILL.md", + "文件要求": "Requirements", + "压缩包或文件夹需包含 SKILL.md": "Archives or folders must contain SKILL.md", + "SKILL.md 需包含 YAML 格式的技能名称和描述": "SKILL.md must include YAML name and description", + "导入时复制完整内容,不修改原始来源": "Import copies full content without modifying source", + "导入到全局 DSH": "Import to global DSH", + "移除所选内容": "Remove selection", + "请选择 .zip 或单个 SKILL.md。": "Please choose a .zip or single SKILL.md.", + "所选文件夹中没有找到 SKILL.md。": "No SKILL.md found in the selected folder.", + + // Creation Form + "例如 code-review-helper": "e.g. code-review-helper", + "一句话说明什么时候使用": "One sentence describing when to use it", + "正文(Markdown)": "Body (Markdown)", + "写下技能要遵循的指令、步骤和边界…": "Write the instructions, steps, and boundaries...", + "创建和导入均保存到全局 DSH,跨项目可用。": "Created and imported skills are saved to global DSH and available across projects.", + + // General Settings & UI "通用设置": "General Settings", "模型设置": "Model Settings", "外观设置": "Appearance", + "语言设置": "Language", "快捷键": "Shortcuts", + "关于": "About", + "版本号": "Version", + "检查新版本": "Check for updates", "语言": "Language", "简体中文": "Simplified Chinese", "英文": "English", + "深色模式": "Dark Mode", + "浅色模式": "Light Mode", + "跟随系统": "Follow System", + "退出": "Exit", + "重启": "Restart", - // Common Notification & Messages - "操作成功": "Operation successful", - "操作失败": "Operation failed", - "更新成功": "Updated successfully", - "删除成功": "Deleted successfully", - "创建成功": "Created successfully", - "导入成功": "Imported successfully", - "备份已保存": "Backup saved", - "正在加载": "Loading", + // Model & Chat Concepts + "模型": "Models", + "会话": "Sessions", + "新会话": "New Session", + "历史会话": "History", + "工作区": "Workspace", + "暂无会话": "No sessions", + "发送": "Send", + "停止生成": "Stop generating", + "重新生成": "Regenerate", + "耗时": "Duration", + "步骤": "Steps", + "轮次": "Turns", }; -// Regex phrase replacements for dynamic strings (e.g. "3 个技能") -export const PHRASE_PATTERNS: Array<[RegExp, string]> = [ +// Sorted array of [chinese, english] pairs, longest Chinese phrases first +// (ensures compound phrases are matched before single words) +export const SORTED_PAIRS: Array<[string, string]> = Object.entries(DICTIONARY).sort( + (a, b) => b[0].length - a[0].length +); + +// Dynamic pattern replacements +export const DYNAMIC_PATTERNS: Array<[RegExp, string]> = [ [/(\d+)\s*个技能/g, "$1 skills"], [/(\d+)\s*个已启用/g, "$1 enabled"], [/(\d+)\s*个已停用/g, "$1 disabled"], [/(\d+)\s*个被覆盖/g, "$1 shadowed"], + [/(\d+)\s*个诊断异常/g, "$1 issues"], [/(\d+)\s*个文件/g, "$1 files"], [/(\d+)\s*个待处理技能/g, "$1 pending skills"], - [/(\d+)\s*个诊断异常/g, "$1 diagnostic issues"], - [/正在检查更新[…\.]*/g, "Checking for updates..."], - [/正在读取说明[…\.]*/g, "Loading documentation..."], - [/暂无可用技能/g, "No skills available"], - [/暂无数据/g, "No data available"], - [/暂无更新/g, "No updates available"], - [/无法恢复/g, "cannot be restored"], - [/已被覆盖/g, "is shadowed"], - [/移入回收站/g, "Move to Trash"], - [/永久删除/g, "Permanently Delete"], + [/来源:/g, "Source: "], + [/创建于\s*([0-9:\-\/ ]+)/g, "Created at $1"], + [/更新于\s*([0-9:\-\/ ]+)/g, "Updated at $1"], + [/删除于\s*([0-9:\-\/ ]+)/g, "Deleted at $1"], + [/被\s*(.+?)\s*覆盖/g, "Shadowed by $1"], ]; diff --git a/src/client/index.ts b/src/client/index.ts index c9ef8fc..d0df065 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -1,14 +1,14 @@ -import { DICTIONARY, PHRASE_PATTERNS } from "./dictionary.js"; +import { DICTIONARY, SORTED_PAIRS, DYNAMIC_PATTERNS } from "./dictionary.js"; export const name = "dsh-plugin-english-client"; -export const inject = ["locale", "slots"]; +export const inject = []; const CHINESE_REGEX = /[\u4e00-\u9fa5]/; /** * Translates a single text string using dictionary and pattern matching. */ -function translateText(text: string): string { +export function translateText(text: string): string { if (!text || !CHINESE_REGEX.test(text)) return text; const trimmed = text.trim(); @@ -17,125 +17,166 @@ function translateText(text: string): string { } let result = text; - // Apply regex patterns - for (const [pattern, replacement] of PHRASE_PATTERNS) { + // Dynamic regex patterns (counts, numbers, time) + for (const [pattern, replacement] of DYNAMIC_PATTERNS) { result = result.replace(pattern, replacement); } - // Exact phrase search for substrings - for (const [cn, en] of Object.entries(DICTIONARY)) { + // Exact phrase search for substrings (longest phrases first) + for (const [cn, en] of SORTED_PAIRS) { if (result.includes(cn)) { result = result.replaceAll(cn, en); + if (!CHINESE_REGEX.test(result)) break; } } return result; } +let isTranslating = false; + /** - * Recursively translates text nodes inside a DOM element. + * Translates an element and all its children using native TreeWalker. */ -function translateNode(node: Node) { - if (node.nodeType === Node.TEXT_NODE) { - const val = node.nodeValue; +export function translateElement(root: Node) { + if (!root) return; + + if (root.nodeType === Node.TEXT_NODE) { + const val = root.nodeValue; if (val && CHINESE_REGEX.test(val)) { const translated = translateText(val); if (translated !== val) { - node.nodeValue = translated; + root.nodeValue = translated; } } return; } - if (node.nodeType === Node.ELEMENT_NODE) { - const el = node as HTMLElement; - const tagName = el.tagName; - // Don't translate code blocks or scripts - if (tagName === "SCRIPT" || tagName === "STYLE" || tagName === "PRE" || tagName === "CODE") { - return; - } + if (root.nodeType !== Node.ELEMENT_NODE) return; + const el = root as HTMLElement; - // Translate common attributes - for (const attr of ["title", "placeholder", "aria-label", "alt"]) { - const val = el.getAttribute(attr); - if (val && CHINESE_REGEX.test(val)) { - el.setAttribute(attr, translateText(val)); + const tag = el.tagName; + if (tag === "SCRIPT" || tag === "STYLE" || tag === "PRE" || tag === "CODE") return; + + // Translate attributes + for (const attr of ["placeholder", "title", "aria-label"]) { + const val = el.getAttribute(attr); + if (val && CHINESE_REGEX.test(val)) { + el.setAttribute(attr, translateText(val)); + } + } + + // Fast native TreeWalker + const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT, null); + let textNode = walker.nextNode(); + while (textNode) { + const val = textNode.nodeValue; + if (val && CHINESE_REGEX.test(val)) { + const parentTag = textNode.parentElement?.tagName; + if (parentTag !== "SCRIPT" && parentTag !== "STYLE" && parentTag !== "PRE" && parentTag !== "CODE") { + const translated = translateText(val); + if (translated !== val) { + textNode.nodeValue = translated; + } } } - - // Translate child nodes - let child = el.firstChild; - while (child) { - translateNode(child); - child = child.nextSibling; - } + textNode = walker.nextNode(); } } -export function apply(ctx: any) { - // 1. Hook DSH ctx.locale to inject English fallback into any registered namespace - if (ctx.locale && typeof ctx.locale.register === "function") { - ctx.effect(() => { - // Whenever locale state changes or plugins register dictionaries, - // hook locale to return English translations for known Chinese keys - if (typeof ctx.locale.subscribe === "function") { - ctx.locale.subscribe(() => { - if (typeof document !== "undefined") { - translateNode(document.body); - } - }); - } - }, "dsh-plugin-english: locale observer"); - } +function initTranslator() { + if (typeof document === "undefined" || (window as any).__DSH_TRANSLATOR_ACTIVE__) return; + (window as any).__DSH_TRANSLATOR_ACTIVE__ = true; - // 2. High-performance DOM MutationObserver for real-time translation - if (typeof document !== "undefined") { - let pending = false; - const queue = new Set(); + console.log("[dsh-plugin-english] Universal English translator activated."); - const flushTranslations = () => { - pending = false; + const queue = new Set(); + let frameRequested = false; + + const processQueue = () => { + frameRequested = false; + isTranslating = true; + try { const nodes = Array.from(queue); queue.clear(); for (const n of nodes) { if (n.isConnected) { - translateNode(n); + translateElement(n); } } - }; + } finally { + isTranslating = false; + } + }; - const observer = new MutationObserver((mutations) => { - for (const m of mutations) { - if (m.type === "childList") { - for (let i = 0; i < m.addedNodes.length; i++) { - const n = m.addedNodes[i]; - if (n.nodeType === Node.TEXT_NODE || n.nodeType === Node.ELEMENT_NODE) { - queue.add(n); - } + const observer = new MutationObserver((mutations) => { + if (isTranslating) return; + + for (const m of mutations) { + if (m.type === "childList") { + for (let i = 0; i < m.addedNodes.length; i++) { + const n = m.addedNodes[i]; + if (n.nodeType === Node.TEXT_NODE || n.nodeType === Node.ELEMENT_NODE) { + queue.add(n); } - } else if (m.type === "characterData") { - queue.add(m.target); - } else if (m.type === "attributes") { + } + } else if (m.type === "characterData") { + if (m.target.nodeValue && CHINESE_REGEX.test(m.target.nodeValue)) { queue.add(m.target); } + } else if (m.type === "attributes") { + queue.add(m.target); } + } - if (!pending && queue.size > 0) { - pending = true; - window.requestAnimationFrame(flushTranslations); - } - }); + if (!frameRequested && queue.size > 0) { + frameRequested = true; + requestAnimationFrame(processQueue); + } + }); - // Initial translation pass on document body - translateNode(document.body); - - // Observe document for dynamically rendered modals, dialogs, and components - observer.observe(document.body, { + // Observe root + const root = document.documentElement || document.body; + if (root) { + translateElement(root); + observer.observe(root, { childList: true, subtree: true, characterData: true, attributes: true, - attributeFilter: ["title", "placeholder", "aria-label", "alt"], + attributeFilter: ["placeholder", "title", "aria-label"], + }); + } else { + document.addEventListener("DOMContentLoaded", () => { + translateElement(document.body); + observer.observe(document.body, { + childList: true, + subtree: true, + characterData: true, + attributes: true, + attributeFilter: ["placeholder", "title", "aria-label"], + }); }); } + + // Periodic safety check every 1s for any dynamically inserted lazy elements + setInterval(() => { + if (document.body && !isTranslating) { + isTranslating = true; + try { + translateElement(document.body); + } finally { + isTranslating = false; + } + } + }, 1000); +} + +// Start immediately when the bundle loads in browser +if (typeof window !== "undefined") { + initTranslator(); +} + +export function apply(ctx: any) { + initTranslator(); }