// ==UserScript== // @name 全能搜索自动翻译助手 (兼容增强版) // @namespace http://tampermonkey.net/ // @version 3.5.1 // @description 在搜索框输入后,按下 Alt+T 自动翻译。适配抖音优化脚本、Google、B站等。 // @author YourName // @match *://*/* // @grant GM_xmlhttpRequest // @run-at document-end // @license 只要您给我署名就可以修改 // @downloadURL https://update.greasyfork.icu/scripts/573874/%E5%85%A8%E8%83%BD%E6%90%9C%E7%B4%A2%E8%87%AA%E5%8A%A8%E7%BF%BB%E8%AF%91%E5%8A%A9%E6%89%8B%20%28%E5%85%BC%E5%AE%B9%E5%A2%9E%E5%BC%BA%E7%89%88%29.user.js // @updateURL https://update.greasyfork.icu/scripts/573874/%E5%85%A8%E8%83%BD%E6%90%9C%E7%B4%A2%E8%87%AA%E5%8A%A8%E7%BF%BB%E8%AF%91%E5%8A%A9%E6%89%8B%20%28%E5%85%BC%E5%AE%B9%E5%A2%9E%E5%BC%BA%E7%89%88%29.meta.js // ==/UserScript== (function() { 'use strict'; // ========================================================================= // ⬇️⬇️⬇️ 用户设置区域 (请在下方修改配置) ⬇️⬇️⬇️ // ========================================================================= // 1. 目标语言 (翻译成什么语言?) // 常用代码:en=英语, zh-CN=中文, ja=日语, ko=韩语, fr=法语, de=德语, ru=俄语, es=西班牙语 const TARGET_LANG = 'en'; // 2. 快捷键字符 (默认为 't') const HOTKEY_CHAR = 't'; // 3. 组合键开关 (true=开启, false=关闭) const USE_ALT = true; // 是否使用 Alt 键 const USE_CTRL = false; // 是否使用 Ctrl 键 const USE_SHIFT = false; // 是否使用 Shift 键 const USE_META = false; // 是否使用 Win/Command 键 // ========================================================================= // ⬆️⬆️⬆️ 设置结束 ⬆️⬆️⬆️ // ========================================================================= // --- 核心逻辑 --- // 使用 capture: true 并在监听器内部做最快判断,防止被“抖音优化”脚本拦截 document.addEventListener('keydown', function(e) { // 1. 快捷键校验 if (!checkHotkey(e)) return; // 2. 获取当前焦点元素 (支持穿越 Shadow DOM) const activeEl = getDeepActiveElement(); if (!activeEl) return; // 3. 元素可编辑性判断 (针对搜索框、评论框) const isEditable = ( ['INPUT', 'TEXTAREA'].includes(activeEl.tagName) || activeEl.isContentEditable || activeEl.getAttribute('contenteditable') === 'true' || activeEl.classList.contains('DraftEditor-editorContainer') // 适配高级编辑器 ); if (!isEditable) return; // 4. 命中逻辑:阻止事件继续传播,防止触发抖音优化脚本或网页原有的快捷键 e.stopImmediatePropagation(); e.preventDefault(); // 获取待翻译文本 let text = getText(activeEl); if (!text || text.trim() === '') return; showToast('正在翻译...'); // 执行翻译 translateText(text, TARGET_LANG) .then(result => { if (result && result !== text) { setInputValue(activeEl, result); showToast('翻译成功 ✅'); } else { showToast('翻译未变或为空', true); } }) .catch(err => { console.error('翻译助手错误:', err); showToast('网络请求失败 ❌', true); }); }, true); /** * 三级赋值策略:专门应对受控组件(React/Vue) * 针对“抖音优化”脚本可能存在的 DOM 修改做了兼容 */ function setInputValue(el, newText) { el.focus(); // [策略 1] 原生 Setter 注入 (破解抖音搜索框 React 拦截的核心) try { const tagName = el.tagName; let nativeSetter; if (tagName === 'INPUT') { nativeSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set; } else if (tagName === 'TEXTAREA') { nativeSetter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, "value").set; } if (nativeSetter) { // 绕过 React 的 value 属性拦截器 nativeSetter.call(el, newText); // 模拟 React 内部值追踪 if (el._valueTracker) { el._valueTracker.setValue(""); } // 派发一套组合拳事件,确保抖音逻辑感知到输入 const eventOptions = { bubbles: true, cancelable: true, composed: true }; el.dispatchEvent(new Event('input', eventOptions)); el.dispatchEvent(new Event('change', eventOptions)); // 抖音特定:部分搜索框需要 blur 触发状态同步,然后重新 focus el.dispatchEvent(new Event('blur', eventOptions)); el.focus(); return; } } catch (e) { console.log("React Hack Error"); } // [策略 2] execCommand (针对 Google 和 contenteditable 元素) try { if (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA') { el.select(); } else { document.execCommand('selectAll', false, null); } if (document.execCommand('insertText', false, newText)) return; } catch (e) {} // [策略 3] 普通赋值 (兜底) if (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA') { el.value = newText; } else { el.innerText = newText; } el.dispatchEvent(new Event('input', { bubbles: true })); } // 获取深层焦点元素 (适配影子 DOM) function getDeepActiveElement() { let el = document.activeElement; while (el && el.shadowRoot && el.shadowRoot.activeElement) { el = el.shadowRoot.activeElement; } return el; } // 获取元素内的文本内容 function getText(el) { if (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA') return el.value; return el.innerText || el.textContent; } // 快捷键逻辑判定 function checkHotkey(e) { // e.key 统一转小写判断 const keyMatch = e.key.toLowerCase() === HOTKEY_CHAR.toLowerCase(); return keyMatch && e.altKey === USE_ALT && e.ctrlKey === USE_CTRL && e.shiftKey === USE_SHIFT && e.metaKey === USE_META; } // 调用 Google 翻译 API function translateText(text, lang) { return new Promise((resolve, reject) => { const url = `https://translate.googleapis.com/translate_a/single?client=gtx&sl=auto&tl=${lang}&dt=t&q=${encodeURIComponent(text)}`; GM_xmlhttpRequest({ method: "GET", url: url, onload: function(r) { if (r.status === 200) { try { const data = JSON.parse(r.responseText); let res = ''; if (data && data[0]) data[0].forEach(i => { if (i[0]) res += i[0]; }); resolve(res); } catch (e) { reject("Parse Error"); } } else { reject("API Error"); } }, onerror: () => reject("Network Error") }); }); } // 提示 UI function showToast(msg, isError = false) { const id = 'tm-translator-toast'; let toast = document.getElementById(id); if (toast) toast.remove(); toast = document.createElement('div'); toast.id = id; Object.assign(toast.style, { position: 'fixed', top: '10%', left: '50%', transform: 'translateX(-50%)', padding: '8px 20px', backgroundColor: isError ? '#ff4d4f' : '#2f2f2f', color: '#fff', borderRadius: '20px', fontSize: '14px', zIndex: '2147483647', pointerEvents: 'none', boxShadow: '0 2px 10px rgba(0,0,0,0.2)', transition: 'opacity 0.3s', opacity: '0' }); toast.innerText = msg; document.documentElement.appendChild(toast); requestAnimationFrame(() => toast.style.opacity = '1'); setTimeout(() => { toast.style.opacity = '0'; setTimeout(() => toast.remove(), 300); }, 1500); } })();