Axiom推文翻译

Automatically translates content and user profiles on specific social platforms using an external translation service.

当前为 2025-07-07 提交的版本,查看 最新版本

// ==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);
        }
    });
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址