Automatically translates content and user profiles on specific social platforms using an external translation service.
当前为
// ==UserScript==
// @name Axiom推文翻译
// @namespace http://tampermonkey.net/
// @version 1.0
// @author @Gufii_666
// @description Automatically translates content and user profiles on specific social platforms using an external translation service.
// @match https://axiom.trade/pulse*
// @match https://axiom.trade/trackers*
// @match https://axiom.trade/meme/*
// @match https://axiom.trade/discover*
// @license MIT
// @grant none
// ==/UserScript==
(function() {
'use strict';
// Constants for translation service
const TRANSLATION_BASE_URL = 'https://translate.googleapis.com/translate_a/single';
const CLIENT_PARAM = 'gtx';
const SOURCE_LANG = 'en';
const TARGET_LANG = 'zh-CN';
const DATA_TYPE = 't';
// Custom CSS class for translated elements
const TRANSLATED_TEXT_CLASS = 'localized-content-display';
const ORIGINAL_DATA_ATTR = 'data-translation-processed-status';
// Utility function to fetch translation
async function obtainLocalizedText(inputString) {
if (!inputString || typeof inputString !== 'string') {
return '[Translation Input Error]';
}
const queryParams = new URLSearchParams({
client: CLIENT_PARAM,
sl: SOURCE_LANG,
tl: TARGET_LANG,
dt: DATA_TYPE,
q: inputString
});
const fullUrl = `${TRANSLATION_BASE_URL}?${queryParams.toString()}`;
try {
const response = await fetch(fullUrl);
const data = await response.json();
return data[0]?.map(segment => segment[0]).join('') || '[Translation Fetch Error]';
} catch (error) {
console.error("Content localization failed:", error);
return '[Failed to Localize]';
}
}
// Helper to create and append translated paragraph
function injectLocalizedParagraph(targetElement, translatedContent, prepend = false) {
if (!targetElement || !targetElement.parentElement) return;
const newParagraph = document.createElement("p");
newParagraph.classList.add(TRANSLATED_TEXT_CLASS);
newParagraph.textContent = translatedContent;
// Apply distinct styling for clarity - Significantly enhanced for visibility
Object.assign(newParagraph.style, {
color: "#FFFFFF", // **白色文本** - 强烈对比
backgroundColor: "#2E8B57", // **深绿色背景** - 醒目且不刺眼
fontSize: "14px", // **略大字体**
fontWeight: "bold", // **加粗**
padding: "8px 12px", // **更多内边距**
borderRadius: "6px", // **更大圆角**
margin: "8px 0", // **更多上下边距**
boxShadow: "0 2px 8px rgba(0, 0, 0, 0.3)", // **添加阴影**,使其浮现
border: "1px solid #4CAF50", // **微妙的边框**
display: "block", // **确保独占一行**
lineHeight: "1.5", // 调整行高
textShadow: "1px 1px 2px rgba(0,0,0,0.2)" // 添加文字阴影,增强可读性
});
if (prepend) {
targetElement.parentElement.insertBefore(newParagraph, targetElement);
} else {
targetElement.parentElement.appendChild(newParagraph);
}
}
// Function to handle content block translation (Tweets and similar content)
async function handleContentBlockTranslation(parentContainer) {
const textElement = parentContainer.querySelector("p.tweet-body_root__ChzUj") ||
parentContainer.querySelector("p.text-textSecondary.mt-1.whitespace-pre-wrap");
if (!textElement || textElement.getAttribute(ORIGINAL_DATA_ATTR)) return;
const rawContent = textElement.innerText.trim();
if (!rawContent) return;
const localizedContent = await obtainLocalizedText(rawContent);
injectLocalizedParagraph(textElement, localizedContent, true);
textElement.setAttribute(ORIGINAL_DATA_ATTR, 'true');
}
// Function to handle user profile description translation
async function handleProfileDescriptionTranslation(containerElement) {
const descriptionElement = containerElement.querySelector("p.break-words");
if (!descriptionElement || descriptionElement.getAttribute(ORIGINAL_DATA_ATTR)) return;
const textParts = Array.from(descriptionElement.querySelectorAll("span"));
const combinedText = textParts.map(s => s.textContent).join('').trim();
if (!combinedText) return;
const localizedContent = await obtainLocalizedText(combinedText);
// For bios, appending might still be fine as it's a dedicated section
injectLocalizedParagraph(descriptionElement, localizedContent, false); // Keep appending for bio
descriptionElement.setAttribute(ORIGINAL_DATA_ATTR, 'true');
// Adjust visibility of containing elements
const parentWrapper = descriptionElement.closest("div[style], div.relative");
if (parentWrapper) {
parentWrapper.style.maxHeight = "none";
parentWrapper.style.overflow = "visible";
const overlayGradient = parentWrapper.querySelector("div[class*='bg-gradient-to-b']");
if (overlayGradient) {
overlayGradient.style.display = "none";
}
}
}
// Main observation mechanism for dynamic content
const contentWatcher = new MutationObserver((mutations) => {
for (const mutationRecord of mutations) {
for (const addedDomNode of mutationRecord.addedNodes) {
if (addedDomNode.nodeType !== 1 || !addedDomNode.querySelector) continue;
// Process tweet-like articles (original structure)
addedDomNode.querySelectorAll("article.tweet-container_article__0ERPK").forEach(handleContentBlockTranslation);
// Process the new type of content block
const newContentBlockParent = addedDomNode.querySelector(".hover\\:bg-primaryStroke\\/20.relative.group.text-\\[14px\\]");
if (newContentBlockParent) {
handleContentBlockTranslation(newContentBlockParent);
}
// Process user bio sections
const potentialBioElement = addedDomNode.querySelector("p.break-words");
if (potentialBioElement) {
const bioContainer = potentialBioElement.closest("div");
if (bioContainer) {
handleProfileDescriptionTranslation(bioContainer);
}
}
}
}
});
contentWatcher.observe(document.body, {
childList: true,
subtree: true
});
// Initial scan for already loaded content
document.querySelectorAll("article.tweet-container_article__0ERPK").forEach(handleContentBlockTranslation);
document.querySelectorAll(".hover\\:bg-primaryStroke\\/20.relative.group.text-\\[14px\\]").forEach(handleContentBlockTranslation);
document.querySelectorAll("p.break-words").forEach(pElement => {
const parentDiv = pElement.closest("div");
if (parentDiv) {
handleProfileDescriptionTranslation(parentDiv);
}
});
})();