您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
支持自动语言检测的划词翻译工具,带可视化界面,适配移动端居中显示
当前为
// ==UserScript== // @name 智能划词翻译工具 // @namespace http://tampermonkey.net/ // @version 1.0 // @description 支持自动语言检测的划词翻译工具,带可视化界面,适配移动端居中显示 // @author Ling // @match *://*/* // @connect fanyi.baidu.com // @grant GM_xmlhttpRequest // @grant GM_addStyle // @grant GM_notification // @description 2025/04/01 19:41:00 // @license MIT // ==/UserScript== (function() { 'use strict'; // 样式注入(优化移动端居中显示) GM_addStyle(` .translation-box { position: fixed; background: #ffffff; border: 1px solid #e0e0e0; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); padding: 16px; max-width: 90vw; width: 320px; z-index: 2147483647; font-family: 'Segoe UI', system-ui, sans-serif; transition: opacity 0.3s; box-sizing: border-box; } .translation-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; } .translation-title { font-weight: 600; color: #2d3748; font-size: 14px; } .translation-close { cursor: pointer; color: #718096; font-size: 18px; line-height: 1; padding: 4px; } .translation-content { line-height: 1.6; color: #4a5568; font-size: 14px; max-height: 50vh; overflow-y: auto; word-break: break-word; } .loading-indicator { display: flex; align-items: center; gap: 8px; } .loading-spinner { width: 16px; height: 16px; border: 2px solid #e2e8f0; border-top-color: #4299e1; border-radius: 50%; animation: spin 1s linear infinite; } @keyframes spin { to { transform: rotate(360deg); } } @media (max-width: 768px) { .translation-box { width: 85vw; padding: 12px; font-size: 13px; left: 50%; transform: translateX(-50%); top: 20%; /* 移动端固定顶部20%位置 */ } .translation-content { font-size: 13px; max-height: 40vh; } } `); // 翻译核心模块(保持不变) const TranslationCore = { async detectLanguage(text) { try { const response = await this._request({ url: 'https://fanyi.baidu.com/langdetect', method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, data: `query=${encodeURIComponent(text)}` }); if (response.error === 0 && response.lan) { return response.lan.toLowerCase(); } throw new Error(response.msg || '检测失败'); } catch (error) { console.warn('语言检测失败:', error); return 'auto'; } }, async translate(text, from = 'auto', to = 'zh') { try { if (from === 'auto') { from = await this.detectLanguage(text) || 'en'; } if (from === 'zh' && to === 'auto') to = 'en'; if (from !== 'zh' && to === 'auto') to = 'zh'; const response = await this._request({ url: 'https://fanyi.baidu.com/ait/text/translate', method: 'POST', headers: { 'Content-Type': 'application/json' }, data: JSON.stringify({ query: text, from: from, to: to, milliTimestamp: Date.now(), domain: "common", needPhonetic: false }) }); return this._parseSSE(response); } catch (error) { console.error('翻译失败:', error); throw error; } }, _parseSSE(rawData) { const events = rawData.split('\n\n').filter(Boolean); const results = []; for (const event of events) { const lines = event.split('\n'); for (const line of lines) { if (line.startsWith('data:')) { try { const data = JSON.parse(line.slice(5).trim()); if (data?.data?.event === 'Translating') { const valid = data.data.list .filter(item => item.dst?.trim()) .map(item => item.dst); results.push(...valid); } } catch (e) { console.warn('SSE解析错误:', e); } } } } return results.length > 0 ? results.join('\n') : '未获取到有效翻译结果'; }, _request(options) { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ ...options, onload: (resp) => { try { resolve(JSON.parse(resp.responseText)); } catch { resolve(resp.responseText); } }, onerror: (err) => reject(err) }); }); } }; // 用户界面控制器(调整定位逻辑) class TranslationUI { constructor() { this.isMobile = window.matchMedia('(max-width: 768px)').matches; this.initDOM(); this.bindEvents(); } initDOM() { this.container = document.createElement('div'); this.container.className = 'translation-box'; this.container.style.display = 'none'; this.container.innerHTML = ` <div class="translation-header"> <span class="translation-title">智能翻译</span> <span class="translation-close">×</span> </div> <div class="translation-content"></div> `; document.body.appendChild(this.container); this.content = this.container.querySelector('.translation-content'); this.closeButton = this.container.querySelector('.translation-close'); } bindEvents() { this.closeButton.onclick = () => this.hide(); document.addEventListener('mousedown', (e) => { if (!this.container.contains(e.target)) this.hide(); }); document.addEventListener('touchstart', (e) => { if (!this.container.contains(e.target)) this.hide(); }); } showLoading() { this.content.innerHTML = ` <div class="loading-indicator"> <div class="loading-spinner"></div> <span>翻译中...</span> </div>`; this.container.style.display = 'block'; } showResult(text) { this.content.innerHTML = text; this.container.style.display = 'block'; this.autoHide(5000); } showError(msg) { this.content.innerHTML = `<div style="color: #e53e3e;">${msg}</div>`; this.container.style.display = 'block'; this.autoHide(3000); } hide() { this.container.style.display = 'none'; } position(x, y) { if (this.isMobile) { // 移动端居中显示,CSS已处理水平居中,垂直位置固定为20% this.container.style.top = '20%'; this.container.style.left = '50%'; this.container.style.transform = 'translateX(-50%)'; } else { // 桌面端基于鼠标/触摸位置 const OFFSET = 15; const rect = this.container.getBoundingClientRect(); let top = y + OFFSET; let left = x + OFFSET; if (left + rect.width > window.innerWidth) { left = Math.max(OFFSET, window.innerWidth - rect.width - OFFSET); } if (top + rect.height > window.innerHeight) { top = Math.max(OFFSET, y - rect.height - OFFSET); } this.container.style.top = `${top}px`; this.container.style.left = `${left}px`; this.container.style.transform = 'none'; // 清除移动端变换 } } autoHide(delay) { clearTimeout(this.hideTimer); this.hideTimer = setTimeout(() => this.hide(), delay); } } function isInputElement(node) { return node && (node.tagName === 'INPUT' || node.tagName === 'TEXTAREA' || node.isContentEditable); } function isSearchBox(node) { return node && node.tagName === 'INPUT' && node.type === 'search'; } let lastSelection = ''; let lastTranslation = ''; let cacheExpireTimer; const MAX_HISTORY = 15; let translationHistory = []; function updateCache(text, translation) { lastSelection = text; lastTranslation = translation; translationHistory = [ { text, translation }, ...translationHistory.slice(0, MAX_HISTORY - 1) ]; clearTimeout(cacheExpireTimer); cacheExpireTimer = setTimeout(() => { lastSelection = ''; lastTranslation = ''; translationHistory = []; }, 1800000); } // 主程序 (function init() { const ui = new TranslationUI(); let debounceTimer = null; const debounce = (func, delay = 300) => { return (...args) => { clearTimeout(debounceTimer); debounceTimer = setTimeout(() => func.apply(this, args), delay); }; }; const handleTranslate = async (x, y, text) => { if (!text) return; const cached = translationHistory.find(item => item.text === text); if (cached) { ui.position(x, y); ui.showResult(cached.translation); return; } ui.currentText = text; ui.position(x, y); ui.showLoading(); try { const result = await TranslationCore.translate(text); updateCache(text, result); ui.showResult(result); } catch (error) { ui.showError(`翻译失败: ${error.message || '服务不可用'}`); GM_notification({ title: '翻译错误', text: error.message, timeout: 3000 }); } }; const handleMouseUp = debounce((e) => { const selection = window.getSelection(); const text = selection.toString().trim(); if (text) handleTranslate(e.pageX, e.pageY, text); }, 150); const handleTouchEnd = debounce((e) => { const selection = window.getSelection(); const text = selection.toString().trim(); if (text) { const touch = e.changedTouches[0]; handleTranslate(touch.pageX, touch.pageY, text); } }, 150); document.addEventListener('mouseup', handleMouseUp); document.addEventListener('touchend', handleTouchEnd); })(); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址