您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
在任意网页上划词翻译,支持中英互译
// ==UserScript== // @name 通用划词翻译 // @namespace http://tampermonkey.net/ // @version 1.1 // @description 在任意网页上划词翻译,支持中英互译 // @author You // @license MIT // @match *://*/* // @grant GM_xmlhttpRequest // @connect translate.googleapis.com // @connect fanyi.youdao.com // @run-at document-idle // ==/UserScript== (function() { 'use strict'; let tooltip = null; let isTranslating = false; // 创建翻译提示框 function createTooltip() { if (tooltip) return; tooltip = document.createElement('div'); tooltip.id = 'translate-tooltip'; tooltip.style.cssText = ` position: absolute !important; background: linear-gradient(135deg, rgba(32, 32, 32, 0.98), rgba(28, 28, 28, 0.98)) !important; color: #e0e0e0 !important; border: 1px solid rgba(255, 255, 255, 0.1) !important; border-radius: 10px !important; padding: 15px 18px !important; font-size: 14px !important; z-index: 999999 !important; box-shadow: 0 10px 35px rgba(0,0,0,0.4), inset 0 1px 0 rgba(255,255,255,0.1) !important; max-width: 350px !important; word-wrap: break-word !important; display: none !important; backdrop-filter: blur(12px) !important; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important; line-height: 1.5 !important; transform-origin: center bottom !important; `; document.body.appendChild(tooltip); } // 检测文本语言 function detectLanguage(text) { // 简单的语言检测:包含中文字符就认为是中文 return /[\u4e00-\u9fa5]/.test(text) ? 'zh' : 'en'; } // 翻译文本 function translateText(text, callback) { if (isTranslating) return; isTranslating = true; const sourceLang = detectLanguage(text); const targetLang = sourceLang === 'zh' ? 'en' : 'zh'; // 使用谷歌翻译API const url = `https://translate.googleapis.com/translate_a/single?client=gtx&sl=${sourceLang}&tl=${targetLang}&dt=t&q=${encodeURIComponent(text)}`; GM_xmlhttpRequest({ method: 'GET', url: url, onload: function(response) { isTranslating = false; try { const result = JSON.parse(response.responseText); const translation = result[0][0][0]; callback(null, translation); } catch (error) { // 如果谷歌翻译失败,尝试使用有道翻译 fallbackTranslate(text, sourceLang, targetLang, callback); } }, onerror: function() { isTranslating = false; // 谷歌翻译失败,尝试有道翻译 fallbackTranslate(text, sourceLang, targetLang, callback); } }); } // 备用翻译方案(有道翻译) function fallbackTranslate(text, sourceLang, targetLang, callback) { const youdaoUrl = `https://fanyi.youdao.com/translate?&doctype=json&type=${sourceLang}2${targetLang}&i=${encodeURIComponent(text)}`; GM_xmlhttpRequest({ method: 'GET', url: youdaoUrl, onload: function(response) { try { const result = JSON.parse(response.responseText); if (result.translateResult && result.translateResult[0] && result.translateResult[0][0]) { const translation = result.translateResult[0][0].tgt; callback(null, translation); } else { callback('翻译失败,请重试', null); } } catch (error) { callback('翻译服务暂时不可用', null); } }, onerror: function() { callback('网络错误,请检查连接', null); } }); } // 显示翻译结果 function showTranslation(rect, originalText, translation, error) { if (!tooltip) createTooltip(); let content = ''; if (error) { content = ` <div style="color: #ff8a80; font-style: italic; text-align: center;">${error}</div> `; } else { content = ` <div style="font-weight: 500; margin-bottom: 10px; color: #ffffff; font-size: 13px; opacity: 0.9;"> ${originalText} </div> <div style="color: #81c784; line-height: 1.5; font-size: 14px;"> ${translation} </div> `; } tooltip.innerHTML = content; // 先设置内容,然后计算位置 tooltip.style.display = 'block'; tooltip.style.visibility = 'hidden'; tooltip.style.transform = 'scale(0.8) translateY(10px)'; tooltip.style.opacity = '0'; const tooltipRect = tooltip.getBoundingClientRect(); const tooltipHeight = tooltipRect.height; const tooltipWidth = tooltipRect.width; // 计算最佳位置(选中文字上方居中,保持距离) let left = rect.left + (rect.width / 2) - (tooltipWidth / 2); let top = rect.top + window.scrollY - tooltipHeight - 12; // 边界检测和调整 const margin = 15; if (left < margin) { left = margin; } else if (left + tooltipWidth > window.innerWidth - margin) { left = window.innerWidth - tooltipWidth - margin; } if (top < window.scrollY + margin) { top = rect.bottom + window.scrollY + 12; } tooltip.style.left = left + 'px'; tooltip.style.top = top + 'px'; tooltip.style.visibility = 'visible'; // 优雅的出现动画 requestAnimationFrame(() => { tooltip.style.transition = 'all 0.25s cubic-bezier(0.34, 1.56, 0.64, 1)'; tooltip.style.transform = 'scale(1) translateY(0)'; tooltip.style.opacity = '1'; }); } // 隐藏翻译提示框 function hideTooltip() { if (tooltip) { tooltip.style.display = 'none'; } } // 获取选中的文本 function getSelectedText() { const selection = window.getSelection(); return selection.toString().trim(); } // 处理文本选择 function handleTextSelection(e) { setTimeout(() => { const selectedText = getSelectedText(); if (selectedText && selectedText.length > 1 && selectedText.length < 500) { // 过滤掉只包含标点符号或数字的选择 if (/^[^\w\u4e00-\u9fa5]+$/.test(selectedText)) { hideTooltip(); return; } const x = e.pageX; const y = e.pageY; // 显示加载状态 showTranslation(x, y, selectedText, '翻译中...', null); // 执行翻译 translateText(selectedText, (error, translation) => { if (error) { showTranslation(x, y, selectedText, null, error); } else { showTranslation(x, y, selectedText, translation, null); } }); } else { hideTooltip(); } }, 100); } // 初始化事件监听 function initializeEventListeners() { // 监听鼠标释放事件(选择文本后) document.addEventListener('mouseup', handleTextSelection); // 监听键盘选择事件 document.addEventListener('keyup', (e) => { if (e.key === 'ArrowLeft' || e.key === 'ArrowRight' || e.key === 'ArrowUp' || e.key === 'ArrowDown' || (e.ctrlKey && e.key === 'a')) { handleTextSelection(e); } }); // 点击其他地方隐藏提示框 document.addEventListener('click', (e) => { if (tooltip && !tooltip.contains(e.target)) { hideTooltip(); } }); // 滚动时隐藏提示框 document.addEventListener('scroll', hideTooltip); // 按ESC键隐藏提示框 document.addEventListener('keydown', (e) => { if (e.key === 'Escape') { hideTooltip(); } }); } // 防止在输入框中触发翻译 function isInputElement(element) { const tagName = element.tagName.toLowerCase(); return tagName === 'input' || tagName === 'textarea' || element.contentEditable === 'true' || element.isContentEditable; } // 改进的文本选择处理 function handleTextSelection(e) { // 如果是在输入框中,不执行翻译 if (isInputElement(e.target)) { hideTooltip(); return; } setTimeout(() => { const selectedText = getSelectedText(); if (selectedText && selectedText.length > 1 && selectedText.length < 500) { // 过滤掉只包含标点符号、数字或空格的选择 if (/^[\s\d\p{P}]+$/u.test(selectedText)) { hideTooltip(); return; } const selection = window.getSelection(); if (selection.rangeCount === 0) { hideTooltip(); return; } const range = selection.getRangeAt(0); const rect = range.getBoundingClientRect(); // 显示加载状态 if (!tooltip) createTooltip(); tooltip.innerHTML = ` <div style="font-weight: 500; margin-bottom: 8px; color: #ffffff; border-bottom: 1px solid rgba(255,255,255,0.2); padding-bottom: 5px;"> ${selectedText} </div> <div style="color: #a0a0a0; font-style: italic;"> 翻译中... </div> `; // 临时显示用于获取尺寸 tooltip.style.display = 'block'; tooltip.style.visibility = 'hidden'; const tooltipRect = tooltip.getBoundingClientRect(); let left = rect.left + (rect.width / 2) - (tooltipRect.width / 2); let top = rect.top + window.scrollY - tooltipRect.height - 15; if (left < 10) left = 10; if (left + tooltipRect.width > window.innerWidth - 10) { left = window.innerWidth - tooltipRect.width - 10; } if (top < window.scrollY + 10) { top = rect.bottom + window.scrollY + 15; } tooltip.style.left = left + 'px'; tooltip.style.top = top + 'px'; tooltip.style.visibility = 'visible'; // 执行翻译 translateText(selectedText, (error, translation) => { if (error) { showTranslation(rect, selectedText, null, error); } else { showTranslation(rect, selectedText, translation, null); } }); } else { hideTooltip(); } }, 100); } // 启动脚本 function init() { createTooltip(); initializeEventListeners(); // 添加样式到页面头部 const style = document.createElement('style'); style.textContent = ` #translate-tooltip { user-select: none !important; pointer-events: auto !important; } #translate-tooltip:hover { transform: scale(1.02) !important; } `; document.head.appendChild(style); } // 等待DOM加载完成后初始化 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();