Greasy Fork

来自缓存

Greasy Fork is available in English.

对照翻译

Display bilingual translations, enable word lookup and pronunciation for English learners on BBC mobile, with a toggle switch using two-finger touch.

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         对照翻译
// @namespace    http://tampermonkey.net/
// @version      1.8.5
// @description  Display bilingual translations, enable word lookup and pronunciation for English learners on BBC mobile, with a toggle switch using two-finger touch.
// @author       You
// @match        *://*/*
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @run-at       document-end
// ==/UserScript==

(function () {
    'use strict';

    // Add style for translation box
    GM_addStyle(`
        .translation-box {
            background: #f8f9fa;
            padding: 5px;
            margin-top: 5px;
            border-left: 3px solid #007bff;
            font-size: 16px;
            color: #333;
            max-width: 100%;
            word-wrap: break-word;
            display: none; /* Initially hidden */
        }
        .show-translation-btn {
            display: inline-block;
            width: 25px;
            height: 25px;
            font-size: 14px;
            background-color: transparent; /* Transparent background */
            color: transparent; /* Transparent text/icon */
            cursor: pointer;
            border: none;
            border-radius: 50%;
            margin-left: 10px;
            position: relative;
            z-index: 1; /* Ensure it's above content */
        }
        .show-translation-btn::after {
            content: "🔘";
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            color: #007bff; /* Icon color */
            font-size: 14px;
        }
    `);

    // API for translation
    const translateAPI = "https://translate.googleapis.com/translate_a/single?client=gtx&sl=en&tl=zh-CN&dt=t&q=";

    // Function to translate text
    const translateText = async (text) => {
        const response = await fetch(translateAPI + encodeURIComponent(text));
        const data = await response.json();
        return data[0][0][0];
    };

    // API for pronunciation (Youdao)
    const youdaoAPI = "https://dict.youdao.com/dictvoice?audio=";

    // Function to play pronunciation
    const playPronunciation = (text) => {
        const audio = new Audio(youdaoAPI + encodeURIComponent(text));
        audio.play();
    };

    // Flag to enable/disable translation globally
    let isTranslationEnabled = false;

    // Function to toggle global translation visibility
    const toggleTranslation = () => {
        isTranslationEnabled = !isTranslationEnabled;
        document.querySelectorAll('.translation-box').forEach(box => {
            box.style.display = isTranslationEnabled ? 'block' : 'none';
        });
    };

    // Translate paragraphs and headings with a toggle button per element
    const addTranslationToElements = async () => {
        const elements = document.querySelectorAll('p, h1, h2, h3, h4, h5, h6');
        for (const el of elements) {
            const text = el.innerText.trim();
            if (text.length > 0) {
                const translation = await translateText(text);

                // Add translation box below the element
                const translationBox = document.createElement('div');
                translationBox.className = 'translation-box';
                translationBox.innerText = translation;
                el.after(translationBox);

                // Add a transparent toggle button for each element
                const toggleBtn = document.createElement('button');
                toggleBtn.className = 'show-translation-btn';
                el.appendChild(toggleBtn);

                // Add event listener to the toggle button
                toggleBtn.addEventListener('click', () => {
                    translationBox.style.display = translationBox.style.display === 'block' ? 'none' : 'block';
                });
            }
        }
    };

    // Initialize translations for paragraphs and headings
    addTranslationToElements();

    // Word lookup and pronunciation functionality on selection
    document.addEventListener('selectionchange', async () => {
        const selection = window.getSelection().toString().trim();
        if (selection) {
            // Play pronunciation directly after selection
            playPronunciation(selection);

            // Translate selected text
            const translation = await translateText(selection);

            // Show translation in a simple alert box
            alert(`${selection}:${translation}`);
        }
    });

    // Handle two-finger touch for translation toggle
    let touchStartTime = 0;

    document.addEventListener('touchstart', (event) => {
        if (event.touches.length === 2) { // Detect two fingers
            touchStartTime = new Date().getTime();
        }
    });

    document.addEventListener('touchend', (event) => {
        if (event.touches.length === 0 && touchStartTime) { // Check if touch was completed
            const touchDuration = new Date().getTime() - touchStartTime;
            if (touchDuration < 500) { // Ensure it’s a short tap
                toggleTranslation(); // Trigger translation toggle
            }
            touchStartTime = 0; // Reset the timer
        }
    });

})();