// ==UserScript== // @name 繁体转简体(使用繁化姬API) // @namespace http://tampermonkey.net/ // @version 0.2 // @description 使用繁化姬API自动将网页繁体中文转换为简体中文 // @author Claude 3.7 // @match *://*/* // @grant GM_xmlhttpRequest // @connect api.zhconvert.org // @run-at document-end // @license MIT // @downloadURL none // ==/UserScript== (function() { 'use strict'; // 声明使用繁化姬API的提示信息 console.log("本程序使用了繁化姬的API服务 - 繁化姬商用必须付费 - https://zhconvert.org"); // 获取用户设置,默认为手动模式 let autoMode = GM_getValue('fanhuaji_auto_mode', false); let converted = GM_getValue('fanhuaji_converted_' + window.location.hostname, false); // 创建一个按钮,用于控制转换功能 const createControlButton = () => { const controlButton = document.createElement('div'); controlButton.id = 'fanhuaji-toggle'; controlButton.style.position = 'fixed'; controlButton.style.bottom = '20px'; controlButton.style.right = '20px'; controlButton.style.padding = '10px'; controlButton.style.backgroundColor = '#f0f0f0'; controlButton.style.border = '1px solid #ccc'; controlButton.style.borderRadius = '5px'; controlButton.style.cursor = 'pointer'; controlButton.style.zIndex = '9999'; controlButton.style.fontSize = '14px'; controlButton.style.fontFamily = 'Arial, sans-serif'; controlButton.style.boxShadow = '0 2px 5px rgba(0,0,0,0.2)'; controlButton.title = "本程序使用了繁化姬的API服务 - 繁化姬商用必须付费"; updateButtonText(controlButton); // 点击按钮执行转换或切换模式 controlButton.addEventListener('click', function(e) { if (e.altKey) { // Alt+点击切换自动/手动模式 autoMode = !autoMode; GM_setValue('fanhuaji_auto_mode', autoMode); updateButtonText(controlButton); showNotification(autoMode ? "已切换到自动模式" : "已切换到手动模式"); } else { // 普通点击执行转换 if (!converted) { convertPage(); converted = true; GM_setValue('fanhuaji_converted_' + window.location.hostname, true); updateButtonText(controlButton); } else { // 如果已经转换过,刷新页面重新加载 converted = false; GM_setValue('fanhuaji_converted_' + window.location.hostname, false); location.reload(); } } controlButton.style.backgroundColor = '#e0e0e0'; setTimeout(() => { controlButton.style.backgroundColor = '#f0f0f0'; }, 300); }); // 添加繁化姬官网链接 const linkElement = document.createElement('a'); linkElement.href = 'https://zhconvert.org'; linkElement.target = '_blank'; linkElement.style.position = 'fixed'; linkElement.style.bottom = '10px'; linkElement.style.right = '20px'; linkElement.style.fontSize = '10px'; linkElement.style.color = '#999'; linkElement.style.textDecoration = 'none'; linkElement.style.zIndex = '9999'; linkElement.textContent = '繁化姬官网'; document.body.appendChild(controlButton); document.body.appendChild(linkElement); return controlButton; }; // 更新按钮文本 const updateButtonText = (button) => { const mode = autoMode ? "自动模式" : "手动模式"; const status = converted ? "已转简体" : "繁→简"; button.innerHTML = `${status} (${mode})
by 繁化姬`; }; // 显示通知 const showNotification = (message) => { const notification = document.createElement('div'); notification.style.position = 'fixed'; notification.style.top = '20px'; notification.style.right = '20px'; notification.style.padding = '10px 15px'; notification.style.backgroundColor = 'rgba(0, 0, 0, 0.7)'; notification.style.color = 'white'; notification.style.borderRadius = '5px'; notification.style.zIndex = '10000'; notification.style.fontSize = '14px'; notification.style.fontFamily = 'Arial, sans-serif'; notification.style.transition = 'opacity 0.5s'; notification.style.boxShadow = '0 2px 10px rgba(0,0,0,0.2)'; notification.textContent = message; document.body.appendChild(notification); setTimeout(() => { notification.style.opacity = '0'; setTimeout(() => { document.body.removeChild(notification); }, 500); }, 2000); }; // 使用繁化姬API转换文本 const convertTextViaAPI = (text, callback) => { if (!text || text.trim() === '') { callback(''); return; } GM_xmlhttpRequest({ method: 'POST', url: 'https://api.zhconvert.org/convert', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, data: 'text=' + encodeURIComponent(text) + '&converter=Simplified', onload: function(response) { try { const result = JSON.parse(response.responseText); if (result.code === 0) { callback(result.data.text); } else { console.error('繁化姬API错误:', result.msg); callback(text); } } catch (e) { console.error('解析API响应出错:', e); callback(text); } }, onerror: function(error) { console.error('API请求失败:', error); callback(text); } }); }; // 获取页面所有文本内容 const getPageText = () => { // 获取body中的所有文本节点内容 const walker = document.createTreeWalker( document.body, NodeFilter.SHOW_TEXT, { acceptNode: function(node) { // 排除script和style标签中的内容 if (node.parentNode.tagName === 'SCRIPT' || node.parentNode.tagName === 'STYLE' || node.parentNode.tagName === 'NOSCRIPT') { return NodeFilter.FILTER_REJECT; } // 如果节点内容不为空,接受该节点 if (node.nodeValue.trim() !== '') { return NodeFilter.FILTER_ACCEPT; } return NodeFilter.FILTER_SKIP; } } ); const textNodes = []; let currentNode; while (currentNode = walker.nextNode()) { textNodes.push(currentNode); } return textNodes; }; // 批量处理文本节点以减少API调用次数 const processTextNodesInBatches = (textNodes, batchSize = 20) => { if (textNodes.length === 0) return; // 显示转换进度 showNotification(`正在转换中 (0/${textNodes.length})`); let processedCount = 0; let lastUpdateTime = Date.now(); // 将文本节点分组 for (let i = 0; i < textNodes.length; i += batchSize) { const batch = textNodes.slice(i, i + batchSize); // 合并批次中的文本内容,使用特殊分隔符 const separator = '|||||'; const combinedText = batch.map(node => node.nodeValue).join(separator); // 调用API转换组合文本 convertTextViaAPI(combinedText, (convertedText) => { // 分割转换后的文本 const convertedParts = convertedText.split(separator); // 更新各个节点的文本内容 for (let j = 0; j < batch.length && j < convertedParts.length; j++) { batch[j].nodeValue = convertedParts[j]; } // 更新进度 processedCount += batch.length; // 每秒最多更新一次进度通知,避免频繁DOM操作 const currentTime = Date.now(); if (currentTime - lastUpdateTime > 1000) { const percent = Math.round((processedCount / textNodes.length) * 100); showNotification(`正在转换中 (${processedCount}/${textNodes.length}, ${percent}%)`); lastUpdateTime = currentTime; } // 所有批次处理完成后显示成功消息 if (processedCount >= textNodes.length) { showNotification("转换完成!"); // 更新按钮状态 const button = document.getElementById('fanhuaji-toggle'); if (button) { updateButtonText(button); } } }); } }; // 转换页面内容 const convertPage = () => { const textNodes = getPageText(); processTextNodesInBatches(textNodes); }; // 检测页面是否主要是繁体中文 const isMainlyTraditionalChinese = () => { const sampleText = getPageText().slice(0, 100).map(node => node.nodeValue).join(''); // 常见的繁体字符 const traditionalChars = '國會學實點體處義務發書樣說語認個開關壹貳參數當爲隻來經與這產紅專麼麗樂營廠鄉兒內馬軍區頭鳥長門問無車電話雞師歲'; // 对应的简体字符 const simplifiedChars = '国会学实点体处义务发书样说语认个开关一二三数当为只来经与这产红专么丽乐营厂乡儿内马军区头鸟长门问无车电话鸡师岁'; let traditionalCount = 0; let simplifiedCount = 0; // 计算繁体和简体字符的数量 for (const char of sampleText) { const traditionalIndex = traditionalChars.indexOf(char); if (traditionalIndex !== -1) { traditionalCount++; } else if (simplifiedChars.indexOf(char) !== -1) { simplifiedCount++; } } // 根据比例判断页面主要是繁体还是简体 return traditionalCount > simplifiedCount && traditionalCount > 5; }; // 监听动态加载的内容 const observeDynamicContent = () => { const observer = new MutationObserver((mutations) => { if (!autoMode || converted) return; let newTextNodes = []; for (const mutation of mutations) { if (mutation.type === 'childList') { for (const node of mutation.addedNodes) { if (node.nodeType === Node.ELEMENT_NODE) { const textNodesInNewContent = getTextNodesIn(node); newTextNodes = newTextNodes.concat(textNodesInNewContent); } } } } if (newTextNodes.length > 0) { processTextNodesInBatches(newTextNodes); } }); observer.observe(document.body, { childList: true, subtree: true }); }; // 获取元素中的所有文本节点 const getTextNodesIn = (element) => { const walker = document.createTreeWalker( element, NodeFilter.SHOW_TEXT, { acceptNode: function(node) { if (node.parentNode.tagName === 'SCRIPT' || node.parentNode.tagName === 'STYLE' || node.parentNode.tagName === 'NOSCRIPT') { return NodeFilter.FILTER_REJECT; } if (node.nodeValue.trim() !== '') { return NodeFilter.FILTER_ACCEPT; } return NodeFilter.FILTER_SKIP; } } ); const textNodes = []; let currentNode; while (currentNode = walker.nextNode()) { textNodes.push(currentNode); } return textNodes; }; // 初始化 const init = () => { // 创建控制按钮 const controlButton = createControlButton(); // 如果启用了自动模式且页面主要是繁体中文,自动转换 if (autoMode && !converted && isMainlyTraditionalChinese()) { convertPage(); converted = true; GM_setValue('fanhuaji_converted_' + window.location.hostname, true); updateButtonText(controlButton); } // 监听动态内容 observeDynamicContent(); }; // 当页面加载完成后初始化 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();