// ==UserScript==
// @name XGO Blockly Translator
// @namespace http://tampermonkey.net/
// @version 0.1
// @description Translate XGO Blockly interface from English to Chinese
// @author Moxiner
// @match http://47.252.22.82:8088/*
// @grant none
// @run-at document-end
// @license None
// ==/UserScript==
(function() {
'use strict';
// 中英对照表
const translations = {
"Please enter your Robot's IP address": "请输入机器人的IP地址",
// 菜单栏
"File": "文件",
"Edit": "编辑",
"View": "视图",
"Format": "格式",
"Tools": "工具",
"Help": "帮助",
// 文件菜单
"New": "新建",
"Open": "打开",
"Save": "保存",
"Save As": "另存为",
"Import": "导入",
"Extras": "导出",
"Export Python": "导出为 Python",
"Themes": "主题",
"Dark": "暗黑主题",
"Light": "亮色主题",
"Default": "重置主题",
"Run": "运行",
"Switch Language": "切换语言",
"Split View": "分屏视图",
"Close": "关闭",
// 编辑菜单
"Undo": "撤销",
"Redo": "重做",
"Duplicate": "复制",
"Add Comment": "添加注释",
"Clear up Blocks": "整理块",
"Delete Block": "删除块",
"Disable Block": "禁用块",
// 视图菜单
"Zoom In": "放大",
"Zoom Out": "缩小",
"Reset Zoom": "重置缩放",
"Blocks": "块",
// 工具菜单
"Settings": "设置",
"Language": "语言",
// 基础
"Basic":"基本",
"Imports":"导入",
"import time":"导入 时间",
"import math":"导入 模块",
"import random": "导入 随机",
"time.sleep(": "时间.睡眠(",
"print(": "打印(",
'print("': '打印("',
"pass": "跳过",
// 逻辑
"Logic": "逻辑",
"and": "与",
"or": "或",
"not": "非",
"None": "无",
"is": "是",
"try": "尝试",
"except": "除外",
"finally": "最后",
// 循环
"Loops": "循环",
"for": "遍历",
"in range(": "在范围内",
"break": "中断",
"in": "在",
"Repeat": "重复",
"times :": "时间:",
"While": "当",
// 条件
"Conditions": "条件",
"condition": "条件",
"if": "如果",
"elif": "那么",
"else": "否则",
// 定义
"Definitions": "定义",
"def": "定义",
"return": "返回",
"class": "类",
"self.": "自身",
// 文本
"Text": "文本",
"text": "文本",
"create text with ": "创建文本时使用",
"to": "到",
"append text": "添加文本",
"length of ": "长度为",
"is empty": "为空",
"in text": "在文本中",
"find": "查找",
"occurrences of text": "文本的出现次数",
"get": "获取",
"trim spaces from ": "去除空格",
"print ": "打印",
// 列表
"Lists": "列表",
// 元组
"Tuple": "元组",
"Tuple": "元组",
// 数学
"Math": "数学",
"math": "数学",
// 变量
"Variables": "变量",
"Create variable...": "创建变量...",
// 屏幕
"Screen": "屏幕",
// 媒体
"Media": "媒体",
"Camera": "摄像头",
"Speaker": "扬声器",
"micphone": "麦克风",
// 键盘
"Key": "键盘",
// 视觉
"Vision": "视觉",
"Voice": "语音",
// XGO-Mini
"XGO-Mini": "XGO-Mini",
// XGO-Lite
"XGO-Lite": "XGO-Lite",
};
// 已处理元素的WeakSet,防止重复处理
const processedElements = new WeakSet();
// 处理状态标志,防止函数重入
let isProcessing = false;
// 翻译函数
function translateText() {
// 如果正在处理中,则直接返回
if (isProcessing) return;
try {
isProcessing = true;
// 获取页面上所有文本节点
const walker = document.createTreeWalker(
document.body,
NodeFilter.SHOW_TEXT,
null,
false
);
const textNodes = [];
let node;
// 收集所有文本节点
while ((node = walker.nextNode())) {
if (node.nodeValue && node.nodeValue.trim()) {
textNodes.push(node);
}
}
// 遍历并翻译文本节点
textNodes.forEach(textNode => {
const text = textNode.nodeValue.trim();
if (translations[text]) {
textNode.nodeValue = textNode.nodeValue.replace(text, translations[text]);
}
});
// 翻译属性中的文本(如placeholder、title等)
const attributesToTranslate = ['placeholder', 'title', 'alt'];
attributesToTranslate.forEach(attr => {
const elements = document.querySelectorAll(`[${attr}]`);
elements.forEach(element => {
const attrValue = element.getAttribute(attr);
if (attrValue && translations[attrValue]) {
element.setAttribute(attr, translations[attrValue]);
}
});
});
// 特殊处理Blockly的SVG文本元素
const blocklyTextElements = document.querySelectorAll('text.blocklyText');
blocklyTextElements.forEach(element => {
// 检查元素是否已经处理过
if (processedElements.has(element)) return;
const textContent = element.textContent;
if (textContent && translations[textContent]) {
element.textContent = translations[textContent];
processedElements.add(element);
}
// 处理包含 的文本(如"import time")
const textWithSpaces = textContent.replace(/\u00A0/g, ' ');
if (translations[textWithSpaces]) {
element.textContent = translations[textWithSpaces];
processedElements.add(element);
}
});
// 处理其他可能的Blockly文本元素
const blocklyDropdownElements = document.querySelectorAll('.blocklyDropdownText');
blocklyDropdownElements.forEach(element => {
if (processedElements.has(element)) return;
const textContent = element.textContent;
if (textContent && translations[textContent]) {
element.textContent = translations[textContent];
processedElements.add(element);
}
});
} finally {
// 无论是否出错都释放处理标志
isProcessing = false;
}
}
// 观察DOM变化并翻译新添加的内容
const observer = new MutationObserver(function(mutations) {
let shouldTranslate = false;
// 检查是否有实际的节点变化
for (const mutation of mutations) {
if (mutation.type === 'childList' &&
(mutation.addedNodes.length > 0 || mutation.removedNodes.length > 0)) {
shouldTranslate = true;
break;
}
}
// 只有在有实际变化时才触发翻译
if (shouldTranslate) {
// 延迟执行以确保DOM更新完成
setTimeout(translateText, 100);
}
});
// 开始观察
observer.observe(document.body, {
childList: true,
subtree: true
});
// 初始翻译
setTimeout(translateText, 1000);
// 定期检查是否有新内容需要翻译 (降低频率以提高性能)
setInterval(translateText, 3000);
})();