// ==UserScript== // @name SN translator // @namespace http://tampermonkey.net/ // @version 0.1 // @description Translate posts on SN // @author You // @match https://stacker.news/* // @icon https://www.google.com/s2/favicons?sz=64&domain=tampermonkey.net // @grant GM_xmlhttpRequest // ==/UserScript== const headers = { origin: 'https://libretranslate.com', accept: '*/*', 'accept-language': 'de-DE,de;q=0.9,ru-DE;q=0.8,ru;q=0.7,en-US;q=0.6,en;q=0.5', 'sec-ch-ua': '"Not?A_Brand";v="8", "Chromium";v="108", "Google Chrome";v="108"', 'sec-ch-ua-mobile': '?0', 'sec-ch-ua-platform': '"Linux"', 'sec-fetch-dest': 'empty', 'sec-fetch-mode': 'cors', 'sec-fetch-site': 'same-origin', }; function translate(text, source, target) { const formData = new FormData(); formData.append('q', text); formData.append('source', source); formData.append('target', target); return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'POST', url: 'https://libretranslate.com/translate', data: formData, headers, synchronous: true, onload: function (res) { const body = JSON.parse(res.responseText); if (res.status !== 200) return reject(body); return resolve(body.translatedText); }, }); }); } const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); const log = (msg) => console.log(`sn-translator:`, msg); function createButton(content) { const btn = document.createElement('button'); btn.innerText = 'Translate'; btn.onclick = async (e) => { const t = await translate(content.innerText, 'auto', 'en').catch(console.error); if (t) content.innerText = t; }; return btn; } function addButtons() { const commentSection = document.querySelector('.item_comments__cN57K'); if (!commentSection) return; const comments = commentSection.querySelectorAll('.comment_comment__5uvl3'); log(`Found ${comments.length} comment(s)`); log(`Adding translate button to every comment ...`); for (const comment of comments) { const topBar = comment.querySelector('.item_other__qNlji'); const content = comment.querySelector('.comment_text__nHI0E'); const padding = document.createElement('span'); padding.innerText = ' '; const btn = createButton(content); topBar.appendChild(padding); topBar.appendChild(btn); } log(`Done`); } (async function () { // Sleep before running script on page load // since else we might get overwritten by loading content. const initialSleep = 1000; await sleep(initialSleep); let pathname = window.location.pathname; log(`Current location: ${pathname}`); addButtons(); // Check if URL changed and rerun script const scriptInterval = 1000; setInterval(() => { if (window.location.pathname !== pathname) { pathname = window.location.pathname; log(`New location detected: ${pathname}`); addButtons(); } }, scriptInterval); })();