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
})();