Greasy Fork

Greasy Fork is available in English.

Twitch Translate Chat Messages

Double-Click to Translate Twitch Chat Messages

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name          Twitch Translate Chat Messages
// @namespace     http://userstyles.org
// @description   Double-Click to Translate Twitch Chat Messages
// @author        636597
// @include       *://*.twitch.tv/*
// @run-at        document-start
// @version       1.0
// ==/UserScript==

var enableTranslation = false;
var destinationLanguage = "en";
// You have to setup some coors enabled https site with
// var GoogleTranslateBase = https://github.com/matheuss/google-translate-api
// or use 
// var GoogleTranslateBase = "https://translation.googleapis.com/language/translate/v2";
// https://console.cloud.google.com/apis/credentials
var gapi_key = "";

// Search for Occurrence *anywhere* in message
var enableBlacklist = false;
var blacklist_anywhere_words = [
];

// Search for these exact words
var blacklist_exact_words = [
    "BTTV!",
    "Prime" ,
    "Subscribe",
    "subscription",
];

function searchBlacklist( wText ) {
    var lower = wText.toLowerCase();
    for ( var i = 0; i < blacklist_anywhere_words.length; ++i ) {
        if ( lower.indexOf( blacklist_anywhere_words[ i ] ) !== -1 ) {
            //console.log( "found anywhere" );
            //console.log( wText );
            return true;
        }
    }
    var x11 = wText.split( ":" )[ 1 ];
    if ( x11 ) {
        x11 = x11.split( " " );
        for ( var j = 0; j < x11.length; ++j ) {
            for ( var i = 0; i < blacklist_exact_words.length; ++i ) {
                if ( x11[ j ] === blacklist_exact_words[ i ] ) {
                    //console.log( "found exact" );
                    //console.log( wText );
                    return true;
                }
            }
        }
    }
    return false;
}

function fixedEncodeURIComponent(str){
    return encodeURIComponent(str).replace(/[!'()]/g, escape).replace(/\*/g, "%2A");
}

var TranslationActive = false;
var gapi_key = "";
var GoogleTranslateEnd = "&key=" + gapi_key;
var TranB1 = "q=";
var TranB2 = "&target=" + destinationLanguage;
function translateText( text , dom_elem ) {
    if ( !text ) { return; }
    if ( !dom_elem ) { return; }
    text = text.trim();
    var query_string = TranB1 + fixedEncodeURIComponent( text ) + TranB2 + GoogleTranslateEnd;
    var anHttpRequest = new XMLHttpRequest();
    anHttpRequest.onreadystatechange = function() {
        if ( anHttpRequest.readyState == 4 && anHttpRequest.status == 200 ) {
            var response = anHttpRequest.responseText;
            console.log( response );
            var translation = JSON.parse( response );
            translation = translation[ "data" ][ "translations" ][ 0 ][ "translatedText" ];
            translation = translation.trim();
            var has_mention_fragment = false;
            var mention_frag_node = null;
            for ( var i = 0; i < dom_elem.childNodes.length; ++i ) {
                if ( dom_elem.childNodes[ i ].className === "mention-fragment" ) { has_mention_fragment = true; mention_frag_node = dom_elem.childNodes[ i ]; }
                var attr = dom_elem.childNodes[ i ].getAttribute( "data-a-target" );
                if ( attr === "chat-message-text" ) {
                    dom_elem.childNodes[ i ].innerHTML = translation;
                    dom_elem.setAttribute( "data-showTranslation" ,  "true" );
                    dom_elem.setAttribute( "data-translatedText" , translation );
                    dom_elem.setAttribute( "data-originalText" ,  text );
                }
            }
            if ( has_mention_fragment ) { dom_elem.removeChild( mention_frag_node ); }
        }
    };
    anHttpRequest.open( "POST", GoogleTranslateBase , true );
    anHttpRequest.setRequestHeader( "Content-Type" , "application/x-www-form-urlencoded; charset=UTF-8" );
    anHttpRequest.send( query_string );
}

var chat_element = null;
var chat_observer = null;
var observerConfig = {
    attributes: true,
    childList: true,
    characterData: true
};
function loadObserver() {
    chat_observer = new MutationObserver(function(mutations) {
        mutations.forEach(function( mutation , index ) {
            if ( mutation.type === "childList" ) {
                var addedNode = mutation.addedNodes[0];
                if( addedNode ) {

                    var msg = addedNode.innerText;

                    // Revert Back to Original
                    if ( TranslationActive ) {
                        addedNode.addEventListener( "dblclick" , function() {
                          var that = this;
                          var show_translation = addedNode.getAttribute( "data-showTranslation" );
                          console.log( show_translation );
                          if ( show_translation ) {
                            if ( show_translation === "true" ) {
                                //that.style.background = "red";
                                console.log( "reverting" );
                                console.log( "old text === " );
                                var original_text = addedNode.getAttribute( "data-originalText" );
                                //console.log( original_text );
                                for ( var i = 0; i < addedNode.childNodes.length; ++i ) {
                                    if ( !addedNode.childNodes[ i ] ) { continue; }
                                    var attr = addedNode.childNodes[ i ].getAttribute( "data-a-target" );
                                    if ( attr === "chat-message-text" ) {
                                        addedNode.childNodes[ i ].innerText = original_text;
                                        break;
                                    }
                                }
                                addedNode.setAttribute( "data-showTranslation" , "false" );
                            }
                            else {
                                //that.style.background = "green";
                                addedNode.setAttribute( "data-showTranslation" ,  "true" );
                                var already_translated = addedNode.getAttribute( "data-translatedText" );
                                //console.log( "already_translated text === " );
                                //console.log( already_translated );                                
                                for ( var i = 0; i < addedNode.childNodes.length; ++i ) {
                                    if ( !addedNode.childNodes[ i ] ) { continue; }
                                    var attr = addedNode.childNodes[ i ].getAttribute( "data-a-target" );
                                    if ( attr === "chat-message-text" ) {
                                        addedNode.childNodes[ i ].innerHTML = already_translated;
                                        break;
                                    }
                                }
                            }
                          }
                          else {
                            //that.style.background = "green";
                            var start = msg.indexOf( ":" );
                            var z1 = msg.substring( ( start + 1 ) );
                            translateText( z1 , addedNode );
                          }
                        } , false );
                    }

                    if ( enableBlacklist ) {
                        var remove = searchBlacklist( msg );

                        // If Not Already Set to be Removed , Search Emotes
                        if ( !remove ) {
                            for ( var i = 0; i < addedNode.childNodes.length; ++i ) {
                                if ( !addedNode.childNodes[ i ] ) { continue; }
                                if ( !remove ) {
                                    var alt_text_target = addedNode.childNodes[ i ].getAttribute( "data-a-target" );
                                    if ( alt_text_target === "emote-name" ) {
                                        for ( var j = 0; j < addedNode.childNodes[ i ].childNodes.length; ++j ) {
                                            if ( !addedNode.childNodes[ i ].childNodes[ j ] ) { continue; }
                                            if ( !remove ) {
                                                if ( addedNode.childNodes[ i ].childNodes[ j ].alt ) {
                                                    var test = searchBlacklist( addedNode.childNodes[ i ].childNodes[ j ].alt );
                                                    if ( test ) {
                                                        remove = true;
                                                        break;                                                    
                                                    }
                                                }
                                            }
                                            else { break; }
                                        }
                                    }
                                }
                                else { break; }
                            }
                        }

                        if ( remove ) {
                            if ( addedNode.parentNode ) {
                                try {
                                    addedNode.setAttribute( "style", "visibility: hidden !important" );
                                    addedNode.setAttribute( "style", "height: 0 !important" );
                                    addedNode.setAttribute( "style", "padding: 0 !important" );
                                    addedNode.innerHTML = "";
                                }
                                catch( e ) { console.log( e ); }
                            }
                        }
                    }
                }
            }
        });
    });

    if ( enableTranslation && gapi_key ) {
        TranslationActive = true;
        console.log( "Translation Option Loaded" );
    }
    if ( TranslationActive || enableBlacklist ) {
        if ( enableBlacklist ) {
            console.log( "Blacklist Option Loaded" );
        }
        chat_observer.observe( chat_element , observerConfig );
    }
}

(function() {
    var ready = setInterval(function(){
        var x1 = document.querySelectorAll( '[role="log"]' );
        if ( x1 ) { if ( x1[ 0 ] ) { chat_element = x1[0]; clearInterval( ready ); loadObserver(); } }
    } , 2 );
})();