diff --git a/lib/client.js b/lib/client.js index ad90be6..64e506c 100644 --- a/lib/client.js +++ b/lib/client.js @@ -244,7 +244,7 @@ var DYNAMIC_PATTERNS = [ // src/client/index.ts var name = "dsh-plugin-english-client"; -var inject = []; +var inject = ["locale"]; var CHINESE_REGEX = /[\u4e00-\u9fa5]/; function translateText(text) { if (!text || !CHINESE_REGEX.test(text)) return text; @@ -252,17 +252,21 @@ function translateText(text) { if (DICTIONARY[trimmed]) { return text.replace(trimmed, DICTIONARY[trimmed]); } - let result = text; for (const [pattern, replacement] of DYNAMIC_PATTERNS) { - result = result.replace(pattern, replacement); + if (pattern.test(trimmed)) { + return text.replace(trimmed, trimmed.replace(pattern, replacement)); + } } + let result = text; + let changed = false; for (const [cn, en] of SORTED_PAIRS) { - if (result.includes(cn)) { + if (cn.length >= 2 && result.includes(cn)) { result = result.replaceAll(cn, en); + changed = true; if (!CHINESE_REGEX.test(result)) break; } } - return result; + return changed ? result : text; } var isTranslating = false; function translateElement(root) { @@ -270,10 +274,8 @@ function translateElement(root) { if (root.nodeType === Node.TEXT_NODE) { const val = root.nodeValue; if (val && CHINESE_REGEX.test(val)) { - const translated = translateText(val); - if (translated !== val) { - root.nodeValue = translated; - } + const t = translateText(val); + if (t !== val) root.nodeValue = t; } return; } @@ -281,7 +283,7 @@ function translateElement(root) { 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"]) { + for (const attr of ["placeholder", "title", "aria-label", "alt"]) { const val = el.getAttribute(attr); if (val && CHINESE_REGEX.test(val)) { el.setAttribute(attr, translateText(val)); @@ -292,33 +294,31 @@ function translateElement(root) { 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; + const parent = textNode.parentElement; + if (parent) { + const pTag = parent.tagName; + if (pTag !== "SCRIPT" && pTag !== "STYLE" && pTag !== "PRE" && pTag !== "CODE") { + const t = translateText(val); + if (t !== val) textNode.nodeValue = t; } } } textNode = walker.nextNode(); } } -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."); +function startDOMTranslator() { + if (typeof document === "undefined") return; + document.documentElement.lang = "en"; const queue = /* @__PURE__ */ new Set(); let frameRequested = false; - const processQueue = () => { + const flush = () => { frameRequested = false; isTranslating = true; try { const nodes = Array.from(queue); queue.clear(); for (const n of nodes) { - if (n.isConnected) { - translateElement(n); - } + if (n.isConnected) translateElement(n); } } finally { isTranslating = false; @@ -329,10 +329,7 @@ function initTranslator() { 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); - } + queue.add(m.addedNodes[i]); } } else if (m.type === "characterData") { if (m.target.nodeValue && CHINESE_REGEX.test(m.target.nodeValue)) { @@ -344,7 +341,7 @@ function initTranslator() { } if (!frameRequested && queue.size > 0) { frameRequested = true; - requestAnimationFrame(processQueue); + requestAnimationFrame(flush); } }); const root = document.documentElement || document.body; @@ -355,18 +352,7 @@ function initTranslator() { subtree: true, characterData: true, attributes: true, - 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"] - }); + attributeFilter: ["placeholder", "title", "aria-label", "alt"] }); } setInterval(() => { @@ -380,11 +366,87 @@ function initTranslator() { } }, 1e3); } -if (typeof window !== "undefined") { - initTranslator(); +function enforceEnglishStorage() { + if (typeof window === "undefined" || !window.localStorage) return; + try { + for (let i = 0; i < localStorage.length; i++) { + const key = localStorage.key(i); + if (key && (key.includes("lang") || key.includes("locale") || key.includes("all-usage") || key.includes("dsh"))) { + const val = localStorage.getItem(key); + if (val === "zh" || val === "zh-CN") { + localStorage.setItem(key, "en"); + } + } + } + } catch { + } } function apply(ctx) { - initTranslator(); + console.log("[dsh-plugin-english] Activating English locale & translation engine..."); + enforceEnglishStorage(); + if (ctx.locale) { + try { + if (typeof ctx.locale.setLocale === "function") { + ctx.locale.setLocale("en"); + } + if (typeof ctx.locale.resolveActive === "function") { + ctx.locale.resolveActive = () => "en"; + } + if (typeof ctx.locale.getSnapshot === "function") { + const origSnap = ctx.locale.getSnapshot.bind(ctx.locale); + ctx.locale.getSnapshot = () => { + const s = origSnap(); + return s ? { ...s, active: "en" } : s; + }; + } + if (typeof ctx.locale.publish === "function") { + ctx.locale.publish("en", true); + } + if (typeof ctx.locale.translate === "function") { + const origTranslate = ctx.locale.translate.bind(ctx.locale); + ctx.locale.translate = function(ns, key, params) { + const val = origTranslate(ns, key, params); + if (typeof val === "string" && CHINESE_REGEX.test(val)) { + return translateText(val); + } + return val; + }; + } + if (ctx.locale.dicts && typeof ctx.locale.dicts.entries === "function") { + for (const [ns, localesMap] of ctx.locale.dicts.entries()) { + const zh = localesMap?.get("zh"); + let en = localesMap?.get("en"); + if (!en) { + en = {}; + localesMap?.set("en", en); + } + if (zh) { + for (const [k, v] of Object.entries(zh)) { + if ((!en[k] || en[k] === k || CHINESE_REGEX.test(en[k])) && typeof v === "string") { + en[k] = translateText(v) || v; + } + } + } + } + } + } catch (err) { + console.error("[dsh-plugin-english] Locale patch error:", err); + } + } + if (typeof document !== "undefined") { + document.documentElement.lang = "en"; + const observer = new MutationObserver(() => { + if (document.documentElement.lang !== "en") { + document.documentElement.lang = "en"; + } + }); + observer.observe(document.documentElement, { attributes: true, attributeFilter: ["lang"] }); + } + startDOMTranslator(); +} +if (typeof window !== "undefined") { + enforceEnglishStorage(); + startDOMTranslator(); } return module.exports; } }); //# sourceMappingURL=client.js.map diff --git a/lib/client.js.map b/lib/client.js.map index c4e508d..d106123 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, 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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", + "sourcesContent": ["import { DICTIONARY, SORTED_PAIRS, DYNAMIC_PATTERNS } from \"./dictionary.js\";\n\nexport const name = \"dsh-plugin-english-client\";\nexport const inject = [\"locale\"];\n\nconst CHINESE_REGEX = /[\\u4e00-\\u9fa5]/;\n\n/**\n * Translates a text string safely without corrupting sentences:\n * 1. Checks exact match on the entire string first.\n * 2. Matches full dynamic count/date patterns.\n * 3. Matches multi-word phrases from longest to shortest (never single characters).\n */\nexport function translateText(text: string): string {\n if (!text || !CHINESE_REGEX.test(text)) return text;\n\n const trimmed = text.trim();\n // 1. Direct exact dictionary match (highest fidelity)\n if (DICTIONARY[trimmed]) {\n return text.replace(trimmed, DICTIONARY[trimmed]);\n }\n\n // 2. Dynamic patterns (e.g. \"3 \u4E2A\u5DF2\u542F\u7528\" -> \"3 enabled\")\n for (const [pattern, replacement] of DYNAMIC_PATTERNS) {\n if (pattern.test(trimmed)) {\n return text.replace(trimmed, trimmed.replace(pattern, replacement));\n }\n }\n\n // 3. Multi-word phrase matches (2+ characters only, longest first)\n let result = text;\n let changed = false;\n for (const [cn, en] of SORTED_PAIRS) {\n if (cn.length >= 2 && result.includes(cn)) {\n result = result.replaceAll(cn, en);\n changed = true;\n if (!CHINESE_REGEX.test(result)) break;\n }\n }\n\n return changed ? result : text;\n}\n\nlet isTranslating = false;\n\n/**\n * Traverses elements using TreeWalker and translates whole text nodes.\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 t = translateText(val);\n if (t !== val) root.nodeValue = t;\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 // Ignore code, pre, scripts, styles\n if (tag === \"SCRIPT\" || tag === \"STYLE\" || tag === \"PRE\" || tag === \"CODE\") return;\n\n // Attributes (placeholder, title, aria-label)\n for (const attr of [\"placeholder\", \"title\", \"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 // 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 parent = textNode.parentElement;\n if (parent) {\n const pTag = parent.tagName;\n if (pTag !== \"SCRIPT\" && pTag !== \"STYLE\" && pTag !== \"PRE\" && pTag !== \"CODE\") {\n const t = translateText(val);\n if (t !== val) textNode.nodeValue = t;\n }\n }\n }\n textNode = walker.nextNode();\n }\n}\n\nfunction startDOMTranslator() {\n if (typeof document === \"undefined\") return;\n\n // Force document language to English immediately\n document.documentElement.lang = \"en\";\n\n const queue = new Set();\n let frameRequested = false;\n\n const flush = () => {\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) translateElement(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 queue.add(m.addedNodes[i]);\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 if (!frameRequested && queue.size > 0) {\n frameRequested = true;\n requestAnimationFrame(flush);\n }\n });\n\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\", \"alt\"],\n });\n }\n\n // Periodic safety check every 1s to sweep any lazily mounted modal content\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// Force English locale in localStorage across known plugin keys\nfunction enforceEnglishStorage() {\n if (typeof window === \"undefined\" || !window.localStorage) return;\n try {\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (key && (key.includes(\"lang\") || key.includes(\"locale\") || key.includes(\"all-usage\") || key.includes(\"dsh\"))) {\n const val = localStorage.getItem(key);\n if (val === \"zh\" || val === \"zh-CN\") {\n localStorage.setItem(key, \"en\");\n }\n }\n }\n } catch {}\n}\n\nexport function apply(ctx: any) {\n console.log(\"[dsh-plugin-english] Activating English locale & translation engine...\");\n\n enforceEnglishStorage();\n\n // 1. Force DSH core locale service to English\n if (ctx.locale) {\n try {\n // Force active locale to \"en\"\n if (typeof ctx.locale.setLocale === \"function\") {\n ctx.locale.setLocale(\"en\");\n }\n if (typeof ctx.locale.resolveActive === \"function\") {\n ctx.locale.resolveActive = () => \"en\";\n }\n\n // Intercept snapshot\n if (typeof ctx.locale.getSnapshot === \"function\") {\n const origSnap = ctx.locale.getSnapshot.bind(ctx.locale);\n ctx.locale.getSnapshot = () => {\n const s = origSnap();\n return s ? { ...s, active: \"en\" } : s;\n };\n }\n\n // Publish active: \"en\" to trigger all native bilingual plugin updates\n if (typeof ctx.locale.publish === \"function\") {\n ctx.locale.publish(\"en\", true);\n }\n\n // Intercept translate function\n if (typeof ctx.locale.translate === \"function\") {\n const origTranslate = ctx.locale.translate.bind(ctx.locale);\n ctx.locale.translate = function (ns: string, key: string, params: any) {\n const val = origTranslate(ns, key, params);\n if (typeof val === \"string\" && CHINESE_REGEX.test(val)) {\n return translateText(val);\n }\n return val;\n };\n }\n\n // Auto-patch all plugin registered dictionaries with English translations\n if (ctx.locale.dicts && typeof ctx.locale.dicts.entries === \"function\") {\n for (const [ns, localesMap] of ctx.locale.dicts.entries()) {\n const zh = localesMap?.get(\"zh\");\n let en = localesMap?.get(\"en\");\n if (!en) {\n en = {};\n localesMap?.set(\"en\", en);\n }\n if (zh) {\n for (const [k, v] of Object.entries(zh)) {\n if ((!en[k] || en[k] === k || CHINESE_REGEX.test(en[k])) && typeof v === \"string\") {\n en[k] = translateText(v) || v;\n }\n }\n }\n }\n }\n } catch (err) {\n console.error(\"[dsh-plugin-english] Locale patch error:\", err);\n }\n }\n\n // 2. Keep document.documentElement.lang set to \"en\"\n if (typeof document !== \"undefined\") {\n document.documentElement.lang = \"en\";\n const observer = new MutationObserver(() => {\n if (document.documentElement.lang !== \"en\") {\n document.documentElement.lang = \"en\";\n }\n });\n observer.observe(document.documentElement, { attributes: true, attributeFilter: [\"lang\"] });\n }\n\n // 3. Start DOM translation engine\n startDOMTranslator();\n}\n\n// Start immediately on script load as well\nif (typeof window !== \"undefined\") {\n enforceEnglishStorage();\n startDOMTranslator();\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 a934fa0..4d4410e 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,9 @@ }, "client": { "immediately": true, + "inject": [ + "@deepseek-ai/dsh-client-locale" + ], "platform": "web" } }, diff --git a/src/client/index.ts b/src/client/index.ts index d0df065..92119f7 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -1,42 +1,50 @@ import { DICTIONARY, SORTED_PAIRS, DYNAMIC_PATTERNS } from "./dictionary.js"; export const name = "dsh-plugin-english-client"; -export const inject = []; +export const inject = ["locale"]; const CHINESE_REGEX = /[\u4e00-\u9fa5]/; /** - * Translates a single text string using dictionary and pattern matching. + * Translates a text string safely without corrupting sentences: + * 1. Checks exact match on the entire string first. + * 2. Matches full dynamic count/date patterns. + * 3. Matches multi-word phrases from longest to shortest (never single characters). */ export function translateText(text: string): string { if (!text || !CHINESE_REGEX.test(text)) return text; const trimmed = text.trim(); + // 1. Direct exact dictionary match (highest fidelity) if (DICTIONARY[trimmed]) { return text.replace(trimmed, DICTIONARY[trimmed]); } - let result = text; - // Dynamic regex patterns (counts, numbers, time) + // 2. Dynamic patterns (e.g. "3 个已启用" -> "3 enabled") for (const [pattern, replacement] of DYNAMIC_PATTERNS) { - result = result.replace(pattern, replacement); + if (pattern.test(trimmed)) { + return text.replace(trimmed, trimmed.replace(pattern, replacement)); + } } - // Exact phrase search for substrings (longest phrases first) + // 3. Multi-word phrase matches (2+ characters only, longest first) + let result = text; + let changed = false; for (const [cn, en] of SORTED_PAIRS) { - if (result.includes(cn)) { + if (cn.length >= 2 && result.includes(cn)) { result = result.replaceAll(cn, en); + changed = true; if (!CHINESE_REGEX.test(result)) break; } } - return result; + return changed ? result : text; } let isTranslating = false; /** - * Translates an element and all its children using native TreeWalker. + * Traverses elements using TreeWalker and translates whole text nodes. */ export function translateElement(root: Node) { if (!root) return; @@ -44,10 +52,8 @@ export function translateElement(root: Node) { if (root.nodeType === Node.TEXT_NODE) { const val = root.nodeValue; if (val && CHINESE_REGEX.test(val)) { - const translated = translateText(val); - if (translated !== val) { - root.nodeValue = translated; - } + const t = translateText(val); + if (t !== val) root.nodeValue = t; } return; } @@ -56,10 +62,11 @@ export function translateElement(root: Node) { const el = root as HTMLElement; const tag = el.tagName; + // Ignore code, pre, scripts, styles if (tag === "SCRIPT" || tag === "STYLE" || tag === "PRE" || tag === "CODE") return; - // Translate attributes - for (const attr of ["placeholder", "title", "aria-label"]) { + // Attributes (placeholder, title, aria-label) + for (const attr of ["placeholder", "title", "aria-label", "alt"]) { const val = el.getAttribute(attr); if (val && CHINESE_REGEX.test(val)) { el.setAttribute(attr, translateText(val)); @@ -72,11 +79,12 @@ export function translateElement(root: Node) { 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; + const parent = textNode.parentElement; + if (parent) { + const pTag = parent.tagName; + if (pTag !== "SCRIPT" && pTag !== "STYLE" && pTag !== "PRE" && pTag !== "CODE") { + const t = translateText(val); + if (t !== val) textNode.nodeValue = t; } } } @@ -84,25 +92,23 @@ export function translateElement(root: Node) { } } -function initTranslator() { - if (typeof document === "undefined" || (window as any).__DSH_TRANSLATOR_ACTIVE__) return; - (window as any).__DSH_TRANSLATOR_ACTIVE__ = true; +function startDOMTranslator() { + if (typeof document === "undefined") return; - console.log("[dsh-plugin-english] Universal English translator activated."); + // Force document language to English immediately + document.documentElement.lang = "en"; const queue = new Set(); let frameRequested = false; - const processQueue = () => { + const flush = () => { frameRequested = false; isTranslating = true; try { const nodes = Array.from(queue); queue.clear(); for (const n of nodes) { - if (n.isConnected) { - translateElement(n); - } + if (n.isConnected) translateElement(n); } } finally { isTranslating = false; @@ -115,10 +121,7 @@ function initTranslator() { 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); - } + queue.add(m.addedNodes[i]); } } else if (m.type === "characterData") { if (m.target.nodeValue && CHINESE_REGEX.test(m.target.nodeValue)) { @@ -128,14 +131,12 @@ function initTranslator() { queue.add(m.target); } } - if (!frameRequested && queue.size > 0) { frameRequested = true; - requestAnimationFrame(processQueue); + requestAnimationFrame(flush); } }); - // Observe root const root = document.documentElement || document.body; if (root) { translateElement(root); @@ -144,22 +145,11 @@ function initTranslator() { subtree: true, characterData: true, attributes: true, - 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"], - }); + attributeFilter: ["placeholder", "title", "aria-label", "alt"], }); } - // Periodic safety check every 1s for any dynamically inserted lazy elements + // Periodic safety check every 1s to sweep any lazily mounted modal content setInterval(() => { if (document.body && !isTranslating) { isTranslating = true; @@ -172,11 +162,104 @@ function initTranslator() { }, 1000); } -// Start immediately when the bundle loads in browser -if (typeof window !== "undefined") { - initTranslator(); +// Force English locale in localStorage across known plugin keys +function enforceEnglishStorage() { + if (typeof window === "undefined" || !window.localStorage) return; + try { + for (let i = 0; i < localStorage.length; i++) { + const key = localStorage.key(i); + if (key && (key.includes("lang") || key.includes("locale") || key.includes("all-usage") || key.includes("dsh"))) { + const val = localStorage.getItem(key); + if (val === "zh" || val === "zh-CN") { + localStorage.setItem(key, "en"); + } + } + } + } catch {} } export function apply(ctx: any) { - initTranslator(); + console.log("[dsh-plugin-english] Activating English locale & translation engine..."); + + enforceEnglishStorage(); + + // 1. Force DSH core locale service to English + if (ctx.locale) { + try { + // Force active locale to "en" + if (typeof ctx.locale.setLocale === "function") { + ctx.locale.setLocale("en"); + } + if (typeof ctx.locale.resolveActive === "function") { + ctx.locale.resolveActive = () => "en"; + } + + // Intercept snapshot + if (typeof ctx.locale.getSnapshot === "function") { + const origSnap = ctx.locale.getSnapshot.bind(ctx.locale); + ctx.locale.getSnapshot = () => { + const s = origSnap(); + return s ? { ...s, active: "en" } : s; + }; + } + + // Publish active: "en" to trigger all native bilingual plugin updates + if (typeof ctx.locale.publish === "function") { + ctx.locale.publish("en", true); + } + + // Intercept translate function + if (typeof ctx.locale.translate === "function") { + const origTranslate = ctx.locale.translate.bind(ctx.locale); + ctx.locale.translate = function (ns: string, key: string, params: any) { + const val = origTranslate(ns, key, params); + if (typeof val === "string" && CHINESE_REGEX.test(val)) { + return translateText(val); + } + return val; + }; + } + + // Auto-patch all plugin registered dictionaries with English translations + if (ctx.locale.dicts && typeof ctx.locale.dicts.entries === "function") { + for (const [ns, localesMap] of ctx.locale.dicts.entries()) { + const zh = localesMap?.get("zh"); + let en = localesMap?.get("en"); + if (!en) { + en = {}; + localesMap?.set("en", en); + } + if (zh) { + for (const [k, v] of Object.entries(zh)) { + if ((!en[k] || en[k] === k || CHINESE_REGEX.test(en[k])) && typeof v === "string") { + en[k] = translateText(v) || v; + } + } + } + } + } + } catch (err) { + console.error("[dsh-plugin-english] Locale patch error:", err); + } + } + + // 2. Keep document.documentElement.lang set to "en" + if (typeof document !== "undefined") { + document.documentElement.lang = "en"; + const observer = new MutationObserver(() => { + if (document.documentElement.lang !== "en") { + document.documentElement.lang = "en"; + } + }); + observer.observe(document.documentElement, { attributes: true, attributeFilter: ["lang"] }); + } + + // 3. Start DOM translation engine + startDOMTranslator(); +} + +// Start immediately on script load as well +if (typeof window !== "undefined") { + enforceEnglishStorage(); + startDOMTranslator(); }