feat: add immediately: true, TreeWalker engine, and comprehensive 500+ term DSH dictionary

This commit is contained in:
katR 2026-10-11 17:25:20 +05:30
parent 519ffefaef
commit f441d0cb12
5 changed files with 514 additions and 270 deletions

View file

@ -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

File diff suppressed because one or more lines are too long

View file

@ -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"
}
},

View file

@ -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<string, string> = {
// 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"],
];

View file

@ -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<Node>();
console.log("[dsh-plugin-english] Universal English translator activated.");
const flushTranslations = () => {
pending = false;
const queue = new Set<Node>();
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();
}