Greasy Fork is available in English.
鼠标悬停自动翻译句子
// ==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();
};