您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
自动翻译 axiom.trade 全站推文内容、用户简介,含主贴+预览+引用推文
当前为
// ==UserScript== // @name axiom 推文翻译 // @namespace https://x.com/Crawford886 // @version 2.3 // @author https://x.com/Crawford886 // @description 自动翻译 axiom.trade 全站推文内容、用户简介,含主贴+预览+引用推文 // @match https://axiom.trade/pulse* // @match https://axiom.trade/meme/* // @match https://axiom.trade/discover* // @license MIT // @grant none // ==/UserScript== (function () { 'use strict'; // ✅ Google Translate 非官方接口 async function translateToChinese(text) { const url = `https://translate.googleapis.com/translate_a/single?client=gtx&sl=auto&tl=zh-CN&dt=t&q=${encodeURIComponent(text)}`; try { const res = await fetch(url); const json = await res.json(); const translated = json[0].map(item => item[0]).join(''); if (translated.length < text.length / 2) { console.warn("翻译结果可能不完整:", text, "=>", translated); } return translated; } catch (err) { console.error("翻译失败:", err); return '[翻译失败]'; } } function isChinese(text) { return /[\u4e00-\u9fa5]/.test(text); } const processedSet = new WeakSet(); // 避免重复翻译的容器 // ✅ 1. 翻译新版推文预览弹窗(优化防重复和多行支持) async function processPreviewPopup(popupElement) { if (popupElement.getAttribute('data-popup-processed')) return; const processWithRetry = async (retryCount = 0) => { const maxRetries = 3; const delay = 200; const mainTweetSpans = popupElement.querySelectorAll('span.text-\\[18px\\].text-wrap.break-words.break-all.text-white:not([data-translated])'); const quoteTweetSpans = popupElement.querySelectorAll('span.text-\\[16px\\].text-wrap.break-words.break-all.text-white:not([data-translated])'); const totalSpans = mainTweetSpans.length + quoteTweetSpans.length; if (totalSpans > 0) { popupElement.setAttribute('data-popup-processed', 'true'); for (const span of mainTweetSpans) { await processTextSpan(span, '18px'); } for (const span of quoteTweetSpans) { await processTextSpan(span, '16px'); } return true; } if (retryCount < maxRetries) { setTimeout(() => processWithRetry(retryCount + 1), delay); return false; } popupElement.setAttribute('data-popup-processed', 'true'); return false; }; await processWithRetry(); } // 处理文本span的翻译(支持长文本分割) async function processTextSpan(span, fontSize) { if (processedSet.has(span)) return; const originalText = span.innerText.trim(); if (!originalText || isChinese(originalText)) { span.setAttribute('data-translated', 'true'); return; } // 检查是否已有翻译元素 const nextSibling = span.nextSibling; if (nextSibling && nextSibling.nodeType === 1 && nextSibling.textContent.includes('🈯️')) { span.setAttribute('data-translated', 'true'); processedSet.add(span); return; } // 标记为正在处理 processedSet.add(span); // 分割长文本为句子或短段 const sentences = originalText.split(/(?<=[.!?])\s+/).filter(s => s.trim()); for (const sentence of sentences) { if (!sentence.trim() || isChinese(sentence.trim())) continue; const translated = await translateToChinese(sentence.trim()); const translatedDiv = document.createElement("div"); translatedDiv.style.color = "rgb(154, 167, 176)"; translatedDiv.style.fontSize = "13px"; translatedDiv.style.marginTop = "6px"; translatedDiv.style.wordBreak = "break-word"; translatedDiv.style.overflowWrap = "anywhere"; translatedDiv.style.whiteSpace = "pre-wrap"; translatedDiv.textContent = "🈯️ " + translated; translatedDiv.setAttribute('data-translation', 'true'); span.parentElement.insertBefore(translatedDiv, span.nextSibling); } span.setAttribute('data-translated', 'true'); } // ✅ 2. 翻译旧版推文预览区域(保持向下兼容) async function processPreviewTweet(tweetElement) { const tweetTextP = tweetElement.querySelector("p.tweet-body_root__ChzUj"); if (!tweetTextP || tweetTextP.getAttribute('data-translated')) return; const originalText = tweetTextP.innerText.trim(); if (!originalText) return; const translated = await translateToChinese(originalText); const translatedP = document.createElement("p"); translatedP.style.color = "rgb(154, 167, 176)"; translatedP.style.fontSize = "13px"; translatedP.style.marginTop = "4px"; translatedP.textContent = "🈯️ " + translated; tweetTextP.parentElement.insertBefore(translatedP, tweetTextP.nextSibling); tweetTextP.setAttribute('data-translated', 'true'); } // ✅ 3. 翻译主贴/引用推文正文(优化推文监控支持) async function processInlineTweet(p) { if (processedSet.has(p) || p.getAttribute('data-translated')) return; const text = p.innerText.trim(); if (!text || isChinese(text)) return; if (p.querySelector('.translated-inline') || p.nextElementSibling?.classList?.contains('translated-inline')) { processedSet.add(p); p.setAttribute('data-translated', 'true'); return; } processedSet.add(p); p.setAttribute('data-translated', 'true'); const translated = await translateToChinese(text); const span = document.createElement('span'); span.className = 'translated-inline'; span.innerText = `🈯️ ${translated}`; span.style.display = 'block'; span.style.color = 'rgb(154, 167, 176)'; span.style.fontSize = '13px'; span.style.marginTop = '3px'; p.insertAdjacentElement('afterend', span); } // ✅ 新增:专门处理推文监控界面 async function processTwitterAlert(alertElement) { const mainTweetPs = alertElement.querySelectorAll('p.text-textSecondary.mt-1.whitespace-pre-wrap:not([data-translated])'); for (const p of mainTweetPs) { await processInlineTweet(p); } const quoteTweetPs = alertElement.querySelectorAll('div.border.border-secondaryStroke p.text-textSecondary.mt-1:not([data-translated])'); for (const p of quoteTweetPs) { await processInlineTweet(p); } } // ✅ 4. 翻译用户简介(保留滚动功能) async function processUserBio(container) { const bioElement = container.querySelector("p.break-words, span.break-words"); if (!bioElement || bioElement.getAttribute("data-translated")) return; const text = bioElement.innerText.trim(); if (!text) return; const translated = await translateToChinese(text); const translatedP = document.createElement("p"); translatedP.style.color = "rgb(154, 167, 176)"; translatedP.style.fontSize = "13px"; translatedP.style.marginTop = "4px"; translatedP.style.whiteSpace = "pre-wrap"; translatedP.textContent = "🈯️ " + translated; bioElement.parentElement.appendChild(translatedP); bioElement.setAttribute("data-translated", 'true'); const wrapper = bioElement.closest("div[style], div.relative"); if (wrapper) { const gradient = wrapper.querySelector("div[class*='bg-gradient-to-b']"); if (gradient) gradient.style.display = "none"; } } // ✅ 统一 MutationObserver(添加推文监控支持) const observer = new MutationObserver((mutationsList) => { for (const mutation of mutationsList) { for (const node of mutation.addedNodes) { if (node.nodeType !== 1 || !node.querySelector) continue; if (node.classList && node.classList.contains('fixed') && node.classList.contains('z-[9999]')) { setTimeout(() => processPreviewPopup(node), 100); } const popups = node.querySelectorAll('div.fixed.z-\\[9999\\].pointer-events-auto'); popups.forEach(popup => { setTimeout(() => processPreviewPopup(popup), 100); }); if (node.classList && (node.classList.contains('cursor-pointer') || node.querySelector('p.text-textSecondary.mt-1'))) { processTwitterAlert(node); } const alertElements = node.querySelectorAll('div[role="button"].cursor-pointer'); alertElements.forEach(processTwitterAlert); node.querySelectorAll("article.tweet-container_article__0ERPK").forEach(processPreviewTweet); const traditionalTweets = node.querySelectorAll("p.text-textSecondary.mt-1:not(.whitespace-pre-wrap)"); traditionalTweets.forEach(p => { if (!p.closest('div[role="button"].cursor-pointer')) { processInlineTweet(p); } }); const bioElement = node.querySelector("p.break-words, span.break-words"); if (bioElement) processUserBio(node); } } }); observer.observe(document.body, { childList: true, subtree: true }); // ✅ 初始扫描(添加推文监控支持) function init() { document.querySelectorAll('div.fixed.z-\\[9999\\].pointer-events-auto').forEach(processPreviewPopup); document.querySelectorAll('div[role="button"].cursor-pointer').forEach(processTwitterAlert); document.querySelectorAll("article.tweet-container_article__0ERPK").forEach(processPreviewTweet); document.querySelectorAll("p.text-textSecondary.mt-1:not(.whitespace-pre-wrap)").forEach(p => { if (!p.closest('div[role="button"].cursor-pointer')) { processInlineTweet(p); } }); document.querySelectorAll("p.break-words, span.break-words").forEach(element => { const container = element.closest("div"); if (container) processUserBio(container); }); } setTimeout(init, 1000); // 延迟初始加载 })();