NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Перевод чата Twitch // @version 0.2 // @description Перевод сообщений в чате с любого языка на русский! // @author MjKey // @match *://*.twitch.tv/* // @icon https://www.google.com/s2/favicons?sz=64&domain=twitch.tv // @copyright 2024, MjKey | MjKey.ru // @grant GM_getValue // @grant GM_setValue // @grant GM_deleteValue // @grant GM_registerMenuCommand // @updateURL https://openuserjs.org/meta/MjKey/Перевод_чата_Twitch.meta.js // @downloadURL https://openuserjs.org/install/MjKey/Перевод_чата_Twitch.user.js // @require https://openuserjs.org/src/libs/sizzle/GM_config.js // @license MIT // @run-at document-end // ==/UserScript== // ==OpenUserJS== // @author MjKey // ==/OpenUserJS== /* global GM_config */ (function() { 'use strict'; console.log("1") GM_config.init({ id: 'ttconf', title: GM_info.script.name + ' • Настройки', fields: { AUTOTRANSLATE: { label: 'Кнопка перевода', type: 'checkbox', default: true, title: 'ВКЛ/ВЫКЛ кнопку' }, FROMLANG: { label: 'На какой язык переводить (ru,nl,fr и т.д.)', type: 'text', default: "ru", title: 'В РАЗРАБОТКЕ' } } }) GM_registerMenuCommand('Настройки', () => { GM_config.open('ttconf') }) GM_registerMenuCommand('Поддержать автора', () => { window.open("https://mjkey.ru/#donate", '_blank'); }) function httpPost(theUrl, theData, callback) { console.log("HTTPPOST") var xmlHttp = new XMLHttpRequest(); xmlHttp.open("POST", theUrl, true); // false for synchronous request xmlHttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xmlHttp.send(theData); xmlHttp.onreadystatechange = function() { if (this.readyState != 4) return; callback(xmlHttp.responseText); } } function isProbablyEnglish(text) { if (!text || text.length === 0 || text.startsWith('!')) return false; // Consider using a library for language detection return !/[А-я]/.test(text); } function TranslateTo(text, to, callback) { console.log(`Translating text: ${text} (to: ${to})`); // Add logging for translation request httpPost('https://translate.googleapis.com/translate_a/single', 'client=gtx&sl=auto&tl='+to+'&dt=t&q='+encodeURI(text), (x) => { try { var jsos = JSON.parse(x); callback({ text: jsos[0][0][0], from: jsos[2] }); console.log(`Translation successful: ${jsos[0][0][0]} (from: ${jsos[2]})`); // Add logging for successful translation } catch (error) { console.error('Error translating text:', error); // Add logging for translation error callback({ text: text, from: 'auto' }); } }, true); } var ChatLanguage = "en"; const chatObserver = new MutationObserver(mutations => { mutations.forEach(mutation => { if (mutation.type == 'childList') { if (mutation.target.matches('.seventv-chat-list') || mutation.target.matches('div.chat-scrollable-area__message-container')) { mutation.addedNodes.forEach(n => { var textns = n.querySelectorAll('.text-fragment') var textn = textns[0]; var textns7 = n.querySelectorAll('.text-token') var textn7 = textns7[0]; setTimeout(() => { if (textn) { var text = ''; textns.forEach(element => { text += element.innerText; }); if (isProbablyEnglish(text.trim()) && text.length > 1 && !text.startsWith('!') && GM_config.get("AUTOTRANSLATE")) { setTimeout(() => { var xnewSpan = document.createElement('span'); xnewSpan.classList.add("chat-line__message--deleted"); xnewSpan.classList.add("twitchtoolbox-translate-button"); xnewSpan.style.cursor = 'pointer'; xnewSpan.innerHTML = '<div class="tw-inline tw-relative tw-tooltip-wrapper" style="float:right;"><div class="tw-align-center tw-inline-block"> ⌈🌐⌋</div></div>'; xnewSpan.title = text; xnewSpan.onclick = function() { var translatedText = ''; var isChanged = false; textns.forEach(element => { element.alt = element.innerText; TranslateTo(element.innerText, GM_config.get("FROMLANG"), (x) => { translatedText += element.innerText = x.text || element.innerText; }); }); if (isChanged) { // Handle successful translation (e.g., update title, show feedback) xnewSpan.title = translatedText; console.log(`Message translation successful: ${translatedText}`); // Add logging for successful message translation } } textn.parentNode.prepend(xnewSpan); }, 0); } } if (textn7) { var text7 = ''; textns7.forEach(element => { text7 += element.innerText; }); if (isProbablyEnglish(text7.trim()) && text7.length > 1 && !text7.startsWith('!') && GM_config.get("AUTOTRANSLATE")) { setTimeout(() => { var xnewSpan = document.createElement('span'); xnewSpan.classList.add("chat-line__message--deleted"); xnewSpan.classList.add("twitchtoolbox-translate-button"); xnewSpan.style.cursor = 'pointer'; xnewSpan.innerHTML = '<div class="tw-inline tw-relative tw-tooltip-wrapper" style="float:right;"><div class="tw-align-center tw-inline-block"> ⌈🌐⌋</div></div>'; xnewSpan.title = text7; xnewSpan.onclick = function() { var translatedText7 = ''; var isChanged = false; textns7.forEach(element => { element.alt = element.innerText; TranslateTo(element.innerText, GM_config.get("FROMLANG"), (x) => { translatedText7 += element.innerText = x.text || element.innerText; }); }); if (isChanged) { // Handle successful translation (e.g., update title, show feedback) xnewSpan.title = translatedText7; console.log(`Message translation successful: ${translatedText7}`); // Add logging for successful message translation } } textn7.parentNode.prepend(xnewSpan); }, 0); } } }); }); } } }); }); chatObserver.observe(document.documentElement, { attributes: true, childList: true, subtree: true }) // Thanks ScriptedEngineer (aka. Siptrixed) <3 })();