您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
✅请先到【扩展管理】中打开【开发人员模式】才能正常使用!✨使用我自己搭建的 translate API,实现划词后自动朗读且显示悬浮翻译卡片,鼠标一旦移出划词区域,立即关闭翻译卡片并清除划词高亮,不想使用该脚本时按 "Ctrl+空格"快捷键 【关闭/开启】该脚本,更轻、更快、更稳定!支持句子、单词翻译,朗读多国语言,国内外皆可使用!
// ==UserScript== // @name 划词朗读翻译 Pro(多国语言流畅朗读版) // @namespace https://wobshare.us.kg // @connect wobys.dpdns.org // @author wob // @version 4.1 // @description ✅请先到【扩展管理】中打开【开发人员模式】才能正常使用!✨使用我自己搭建的 translate API,实现划词后自动朗读且显示悬浮翻译卡片,鼠标一旦移出划词区域,立即关闭翻译卡片并清除划词高亮,不想使用该脚本时按 "Ctrl+空格"快捷键 【关闭/开启】该脚本,更轻、更快、更稳定!支持句子、单词翻译,朗读多国语言,国内外皆可使用! // @match *://*/* // @exclude *://www.google.com/search* // @grant GM_xmlhttpRequest // @grant GM_addStyle // @license MIT // ==/UserScript== (function () { 'use strict'; let voiceReady = false; let cachedVoices = []; let scriptEnabled = true; // 脚本的启用状态,默认为开启 // --- 语音预加载 --- function preloadVoices() { cachedVoices = speechSynthesis.getVoices(); if (cachedVoices.length) voiceReady = true; } speechSynthesis.onvoiceschanged = () => { cachedVoices = speechSynthesis.getVoices(); if (cachedVoices.length) voiceReady = true; }; preloadVoices(); // --- CSS 样式 --- GM_addStyle(` .translate-tooltip { position: absolute; background: linear-gradient(135deg, #4A90E2, #007AFF); color: #fff; padding: 10px 14px; border-radius: 12px; font-size: 15px; max-width: 360px; box-shadow: 0 4px 12px rgba(0,0,0,0.25); white-space: pre-line; font-family: "Segoe UI", Roboto, "Helvetica Neue", Arial; pointer-events: auto; z-index: 9999; } /* 提示消息样式 */ .userscript-toast { position: fixed; bottom: 20px; right: 20px; background-color: rgba(0, 0, 0, 0.75); color: white; padding: 10px 15px; border-radius: 8px; font-size: 14px; z-index: 10000; opacity: 0; transition: opacity 0.3s ease-in-out; pointer-events: none; /* 确保不影响页面交互 */ } .userscript-toast.show { opacity: 1; } `); let selectionBox = null; // 用于存储选区位置信息 // --- 主鼠标抬起事件处理函数 --- // 这个函数会在脚本启用时被监听 function handleMouseUp() { if (!scriptEnabled) return; // 如果脚本被禁用,则直接返回 const text = window.getSelection().toString().trim(); if (!text || text.length > 200) return; // 检查文本有效性和长度 const range = window.getSelection().getRangeAt(0); const rect = range.getBoundingClientRect(); selectionBox = rect; // 记录选区位置 speakViaBrowser(text); // 朗读 fetchTranslation(text, rect); // 翻译 document.addEventListener('mousemove', strictMouseLeaveCheck); // 添加鼠标移动监听器以检测鼠标移出 } // 初始时添加 mouseup 监听器 document.addEventListener('mouseup', handleMouseUp); // --- 语音朗读函数 --- function speakViaBrowser(text) { if (!scriptEnabled) return; // 如果脚本被禁用,不执行朗读 if (!voiceReady) return; // 检查语音是否准备好 // 尝试查找语音,如果没有则使用第一种可用语音 const voice = cachedVoices.find(v => v.lang === 'ko-KR') || cachedVoices[0]; if (!voice) return; // 如果没有可用语音,则返回 const utter = new SpeechSynthesisUtterance(text); utter.voice = voice; utter.lang = voice.lang || 'ko-KR'; // 设置朗读语言 speechSynthesis.cancel(); // 取消当前所有朗读 speechSynthesis.speak(utter); // 开始朗读 } // --- 翻译请求函数 --- function fetchTranslation(text, rect) { if (!scriptEnabled) return; // 如果脚本被禁用,不执行翻译请求 GM_xmlhttpRequest({ method: 'GET', url: `https://wobys.dpdns.org/?text=${encodeURIComponent(text)}&source_language=ko&target_language=zh&secret=123456`, onload: res => { try { const data = JSON.parse(res.responseText); const output = data.text || '无翻译结果'; showTooltip(`📘 𝓌𝑜𝒷翻译:\n${output}`, rect); // 显示翻译结果 } catch (err) { showTooltip('🌐 翻译解析失败', rect); // 解析失败提示 } }, onerror: () => { showTooltip('🌐 翻译请求失败', rect); // 请求失败提示 } }); } // --- 翻译卡片管理 --- function showTooltip(text, rect) { if (!scriptEnabled) return; // 如果脚本被禁用,不显示卡片 removeTooltip(); // 先移除旧的卡片 const tip = document.createElement('div'); tip.className = 'translate-tooltip'; tip.innerText = text; document.body.appendChild(tip); // 设置卡片位置 tip.style.left = `${rect.left + window.scrollX}px`; tip.style.top = `${rect.bottom + window.scrollY + 10}px`; } // 严格的鼠标移出检测 function strictMouseLeaveCheck(e) { if (!selectionBox) return; // 如果没有选区,则返回 const { left, right, top, bottom } = selectionBox; const buffer = 5; // 增加一个小的缓冲区域 // 判断鼠标是否在选区范围内(包括缓冲区域) const inArea = e.pageX >= left + window.scrollX - buffer && e.pageX <= right + window.scrollX + buffer && e.pageY >= top + window.scrollY - buffer && e.pageY <= bottom + window.scrollY + buffer; if (!inArea) { removeTooltip(); // 移除翻译卡片 document.removeEventListener('mousemove', strictMouseLeaveCheck); // 移除鼠标移动监听 selectionBox = null; // 清空选区位置信息 if (window.getSelection) { window.getSelection().removeAllRanges(); // 清空选中文本(若不想清空选中的文本,可注释此行,多行注释:Ctrl+/ 或 Ctrl+K ) } } } function removeTooltip() { const el = document.querySelector('.translate-tooltip'); if (el) el.remove(); // 移除翻译卡片DOM元素 } // --- 提示消息函数 --- function showToast(message) { let toast = document.querySelector('.userscript-toast'); if (!toast) { toast = document.createElement('div'); toast.className = 'userscript-toast'; document.body.appendChild(toast); } toast.innerText = message; toast.classList.add('show'); // 显示提示 setTimeout(() => { toast.classList.remove('show'); // 2秒后隐藏提示 }, 2000); } // --- 脚本启用/禁用切换逻辑 --- function toggleScriptEnabled() { scriptEnabled = !scriptEnabled; // 切换状态 if (scriptEnabled) { // 脚本开启时 document.addEventListener('mouseup', handleMouseUp); // 添加 mouseup 监听 showToast('划词朗读翻译 Pro 已开启'); } else { // 脚本关闭时 document.removeEventListener('mouseup', handleMouseUp); // 移除 mouseup 监听 removeTooltip(); // 移除当前显示的翻译卡片 speechSynthesis.cancel(); // 停止所有正在进行的朗读 document.removeEventListener('mousemove', strictMouseLeaveCheck); // 移除鼠标移出检测 selectionBox = null; // 清空选区信息 if (window.getSelection) { window.getSelection().removeAllRanges(); } showToast('划词朗读翻译 Pro 已关闭'); } } // --- 键盘事件监听器,用于检测 Ctrl + Space --- document.addEventListener('keydown', (e) => { // 检查是否按下了 Ctrl 键和空格键 (e.code === 'Space' 兼容性更好) if (e.ctrlKey && e.code === 'Space') { e.preventDefault(); // 阻止浏览器默认的 Ctrl+Space 行为 (例如某些系统的输入法切换) toggleScriptEnabled(); // 切换脚本状态 } }); // 脚本加载完成后,显示初始提示 showToast('划词朗读翻译 Pro 已开启 (Ctrl + Space 切换)'); })();