Greasy Fork is available in English.
点击按钮将当前网页所有英文翻译成简体中文(高速、无残留分隔符)
// ==UserScript==
// @name 一键中英翻译切换器(网页整页翻译)- 高速无残留版
// @namespace http://tampermonkey.net/1554493
// @version 1.2
// @description 点击按钮将当前网页所有英文翻译成简体中文(高速、无残留分隔符)
// @author 飞翔的荷兰人269
// @match *://*/*
// @grant none
// @run-at document-end
// @license MIT
// ==/UserScript==
(function() {
'use strict';
let isTranslated = false;
let originalTexts = new Map();
// 创建按钮(左下角)
let button = document.createElement('button');
button.innerText = '翻译为中文';
button.style.position = 'fixed';
button.style.bottom = '20px';
button.style.left = '20px';
button.style.zIndex = '999999';
button.style.padding = '12px 18px';
button.style.backgroundColor = '#4285f4';
button.style.color = 'white';
button.style.border = 'none';
button.style.borderRadius = '8px';
button.style.cursor = 'pointer';
button.style.fontSize = '15px';
button.style.fontWeight = 'bold';
button.style.boxShadow = '0 4px 12px rgba(0,0,0,0.3)';
button.style.transition = 'all 0.3s';
document.body.appendChild(button);
// Google 翻译 API
async function translateText(text) {
if (!text || !/[a-zA-Z]/.test(text)) return text;
try {
const response = await fetch(`https://translate.googleapis.com/translate_a/single?client=gtx&sl=en&tl=zh-CN&dt=t&q=${encodeURIComponent(text)}`);
const data = await response.json();
return data[0].map(item => item[0]).join('');
} catch (e) {
console.error('翻译失败:', e);
return text;
}
}
// 判断节点是否需要翻译
function shouldTranslate(node) {
if (!node.parentElement) return false;
const tag = node.parentElement.tagName;
if (['SCRIPT', 'STYLE', 'NOSCRIPT', 'CODE', 'PRE', 'HEADER', 'NAV', 'FOOTER'].includes(tag)) return false;
const className = node.parentElement.className.toLowerCase();
if (className.includes('menu') || className.includes('nav') || className.includes('header') || className.includes('footer')) return false;
return true;
}
// 翻译整个页面(修复分隔符问题)
async function translatePage() {
const textsToTranslate = []; // {node, original: text, trimmed}
function walk(node) {
if (node.nodeType === Node.TEXT_NODE) {
const text = node.textContent;
const trimmed = text.trim();
if (trimmed && /[a-zA-Z]/.test(trimmed) && shouldTranslate(node)) {
textsToTranslate.push({node, original: text, trimmed});
if (!originalTexts.has(node)) {
originalTexts.set(node, text);
}
}
} else {
for (let child of node.childNodes) walk(child);
}
}
walk(document.body);
if (textsToTranslate.length === 0) {
alert('页面没有需要翻译的英文内容');
return;
}
button.innerText = `翻译中... (0/${textsToTranslate.length})`;
button.disabled = true;
// 分批翻译(每批50条,用 \n\n 分隔,Google 会保留并正确处理)
const batchSize = 50;
for (let i = 0; i < textsToTranslate.length; i += batchSize) {
const batch = textsToTranslate.slice(i, i + batchSize);
const combinedText = batch.map(item => item.trimmed).join('\n\n');
const translatedCombined = await translateText(combinedText);
// 按 \n\n 拆分,确保顺序一致
const translatedParts = translatedCombined.split('\n\n');
// 写回节点(如果拆分数量不匹配,用最后一个或原文兜底)
batch.forEach((item, idx) => {
let translated = translatedParts[idx] || translatedParts[translatedParts.length - 1] || item.trimmed;
translated = translated.trim(); // 去除多余空格
if (translated && translated !== item.trimmed) {
const leading = item.original.match(/^(\s*)/)[0];
const trailing = item.original.match(/(\s*)$/)[0];
item.node.textContent = leading + translated + trailing;
}
});
button.innerText = `翻译中... (${Math.min(i + batchSize, textsToTranslate.length)}/${textsToTranslate.length})`;
}
button.innerText = '恢复原文';
button.style.backgroundColor = '#db4437';
button.disabled = false;
isTranslated = true;
}
// 恢复原文
function restorePage() {
originalTexts.forEach((originalText, node) => {
if (node.parentNode) node.textContent = originalText;
});
originalTexts.clear();
button.innerText = '翻译为中文';
button.style.backgroundColor = '#4285f4';
isTranslated = false;
}
button.addEventListener('click', () => {
if (isTranslated) {
restorePage();
} else {
translatePage();
}
});
console.log('高速无残留网页翻译器已加载(左下角按钮)');
})();