// ==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)) function addButtons() { console.log('sn-translator: Adding button to every comment ...') const commentSection = document.querySelector('.item_comments__cN57K') const comments = commentSection.querySelectorAll('.comment_comment__5uvl3') 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 = 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 } topBar.appendChild(padding) topBar.appendChild(btn) } console.log('Done') } ;(async function () { await sleep(1000) addButtons() })()