Greasy Fork

Greasy Fork is available in English.

悬停翻译句子

鼠标悬停自动翻译句子

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         悬停翻译句子
// @namespace    http://tampermonkey.net/
// @version      1.3
// @description  鼠标悬停自动翻译句子
// @author       pipizhu
// @match        http*://*/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @license 		 MIT
// ==/UserScript==

function wrapTextNodes(parent) {
  // Create a document fragment to hold the new structure
  const fragment = document.createDocumentFragment();
  const content = parent.innerHTML;
  function splitSentences(text) {
    if (!text) return [];
    // const segmenter = new Intl.Segmenter("en", { granularity: "sentence" });
    // return Array.from(segmenter.segment(text), (segment) => segment.segment);
    const regex =
      /(?=[^])(?:\P{Sentence_Terminal}|\p{Sentence_Terminal}(?!['"`\p{Close_Punctuation}\p{Final_Punctuation}\s]))*(?:\p{Sentence_Terminal}+['"`\p{Close_Punctuation}\p{Final_Punctuation}]*|$)/guy;
    return text.match(regex);
  }
  const sentences = splitSentences(content);

  sentences.forEach((sentence) => {
    const span = document.createElement("span");
    span.className = "sentence";
    span.innerHTML = sentence;
    parent.innerHTML = "";
    fragment.appendChild(span);
    fragment.appendChild(document.createTextNode(" ")); // Add space between sentences
  });

  return fragment;
}

const getDivs = () => {
  const targets = ["#text", "A", "BR"];
  const list = [...document.querySelectorAll("div")].filter(
    (d) =>
      d.textContent &&
      d.textContent.length > 15 &&
      ![...d.childNodes].find((n) => !targets.includes(n.nodeName)),
  );
  console.log("div list", list);
  list.forEach((d) => {
    if (document.documentElement.lang.includes("en")) {
      d.appendChild(wrapTextNodes(d));
    }
  });
};

const translateText = async (text, targetLanguage) => {
  return googleTranslate(text, targetLanguage);
};

const googleTranslate = async (text, targetLanguage) => {
  try {
    const url = `https://translate.googleapis.com/translate_a/t?client=gtx&sl=en&tl=${targetLanguage}&dt=t&q=${encodeURIComponent(
      text,
    )}&format=html`;
    const response = await fetch(url, {
      method: "POST",
    });
    const data = await response.json();
    return data[0];
  } catch (error) {
    console.error("There was a problem with the fetch operation:", error);
  }
};

function init() {
  getDivs();
  // Get all <p> tags on the page
  const paragraphs = document.querySelectorAll("p, h1, h2, h3, h4");

  // Iterate through each <p> tag
  paragraphs.forEach((paragraph) => {
    if (document.documentElement.lang.includes("en")) {
      paragraph.appendChild(wrapTextNodes(paragraph));
    }
  });

  // Add a style element to the document head for the hover effect
  const style = document.createElement("style");
  style.innerHTML = `
    .sentence {
        position: relative;
        transition: border-bottom-color 0.3s ease;
        cursor: pointer;
    }
    .sentence:hover {
        border-bottom: 2px solid #32CD32; /* Beautiful green */
    }
    .t-popover {
        position: absolute;
        color: black;
        top: 100%;
        width: 100%;
        left: 0;
        min-width: 300px;
        background-color: #fff;
        padding: 5px 10px;
        border: 1px solid #ccc;
        border-radius: 3px;
        box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
        z-index: 1000;
        transform: translateY(10px);
        display: none;
    }
    .sentence:hover .t-popover {
        display: block;
    }
`;
  document.head.appendChild(style);

  // Apply the hover effect and translation using JavaScript
  document.querySelectorAll(".sentence").forEach((span) => {
    const greenColor = "#32CD32"; // Beautiful green color

    // Create a popover element
    const popover = document.createElement("div");
    popover.className = "t-popover";
    span.appendChild(popover);

    span.addEventListener("mouseover", function () {
      this.style.borderBottomColor = greenColor;
      // Check if the popover already contains translated text
      if (popover.innerText.trim() !== "") {
        return; // If it does, do nothing
      }

      // Get the original text
      const originalText = this.innerText;
      console.log("span", this.innerText);
      translateText(originalText, "zh")
        .then((data) => {
          console.log("result ", data);
          // Show the translated text in the popover
          popover.innerHTML = data;
        })
        .catch((error) => {
          popover.innerText = "Translation failed";
          console.error("Error:", error);
        });
    });

    span.addEventListener("mouseout", function () {
      this.style.borderBottomColor = "transparent";
    });
  });
}

// Function to handle route changes and initialize the script
const handleRouteChange = () => {
  init();
};

// Call init() on initial page load
init();

// Listen for route changes in single-page applications
if (typeof window.addEventListener === "function") {
  window.addEventListener("popstate", handleRouteChange);
  window.addEventListener("pushState", handleRouteChange);
  window.addEventListener("replaceState", handleRouteChange);
}

// Monkey patch history methods for better SPA support
const originalPushState = history.pushState;
const originalReplaceState = history.replaceState;

history.pushState = function () {
  originalPushState.apply(this, arguments);
  handleRouteChange();
};

history.replaceState = function () {
  originalReplaceState.apply(this, arguments);
  handleRouteChange();
};