Language Switcher for Google™

a port of Language Switcher for Google™ Chome extension (https://chrome.google.com/webstore/detail/language-switcher-for-goo/jbbappjmafnnhhalfbhdhiemchcgejcp) to userscript.js

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name        Language Switcher for Google™
// @author      gb.ianni shunf4
// @namespace   gb.ianni
// @version     2.3.4
// @copyright   Copyright 2015-2022 gb.ianni shunf4
// @description a port of Language Switcher for Google™ Chome extension (https://chrome.google.com/webstore/detail/language-switcher-for-goo/jbbappjmafnnhhalfbhdhiemchcgejcp) to userscript.js
// @include *://www.google.com/*
// @include *://www.google.ad/*
// @include *://www.google.ae/*
// @include *://www.google.com.af/*
// @include *://www.google.com.ag/*
// @include *://www.google.com.ai/*
// @include *://www.google.am/*
// @include *://www.google.co.ao/*
// @include *://www.google.com.ar/*
// @include *://www.google.as/*
// @include *://www.google.at/*
// @include *://www.google.com.au/*
// @include *://www.google.az/*
// @include *://www.google.ba/*
// @include *://www.google.com.bd/*
// @include *://www.google.be/*
// @include *://www.google.bf/*
// @include *://www.google.bg/*
// @include *://www.google.com.bh/*
// @include *://www.google.bi/*
// @include *://www.google.bj/*
// @include *://www.google.com.bn/*
// @include *://www.google.com.bo/*
// @include *://www.google.com.br/*
// @include *://www.google.bs/*
// @include *://www.google.co.bw/*
// @include *://www.google.by/*
// @include *://www.google.com.bz/*
// @include *://www.google.ca/*
// @include *://www.google.cd/*
// @include *://www.google.cf/*
// @include *://www.google.cg/*
// @include *://www.google.ch/*
// @include *://www.google.ci/*
// @include *://www.google.co.ck/*
// @include *://www.google.cl/*
// @include *://www.google.cm/*
// @include *://www.google.cn/*
// @include *://www.google.com.co/*
// @include *://www.google.co.cr/*
// @include *://www.google.com.cu/*
// @include *://www.google.cz/*
// @include *://www.google.de/*
// @include *://www.google.dj/*
// @include *://www.google.dk/*
// @include *://www.google.dm/*
// @include *://www.google.com.do/*
// @include *://www.google.dz/*
// @include *://www.google.com.ec/*
// @include *://www.google.ee/*
// @include *://www.google.com.eg/*
// @include *://www.google.es/*
// @include *://www.google.com.et/*
// @include *://www.google.fi/*
// @include *://www.google.com.fj/*
// @include *://www.google.fm/*
// @include *://www.google.fr/*
// @include *://www.google.ga/*
// @include *://www.google.ge/*
// @include *://www.google.gg/*
// @include *://www.google.com.gh/*
// @include *://www.google.com.gi/*
// @include *://www.google.gl/*
// @include *://www.google.gm/*
// @include *://www.google.gp/*
// @include *://www.google.gr/*
// @include *://www.google.com.gt/*
// @include *://www.google.gy/*
// @include *://www.google.com.hk/*
// @include *://www.google.hn/*
// @include *://www.google.hr/*
// @include *://www.google.ht/*
// @include *://www.google.hu/*
// @include *://www.google.co.id/*
// @include *://www.google.ie/*
// @include *://www.google.co.il/*
// @include *://www.google.im/*
// @include *://www.google.co.in/*
// @include *://www.google.iq/*
// @include *://www.google.is/*
// @include *://www.google.it/*
// @include *://www.google.je/*
// @include *://www.google.com.jm/*
// @include *://www.google.jo/*
// @include *://www.google.co.jp/*
// @include *://www.google.co.ke/*
// @include *://www.google.com.kh/*
// @include *://www.google.ki/*
// @include *://www.google.kg/*
// @include *://www.google.co.kr/*
// @include *://www.google.com.kw/*
// @include *://www.google.kz/*
// @include *://www.google.la/*
// @include *://www.google.com.lb/*
// @include *://www.google.li/*
// @include *://www.google.lk/*
// @include *://www.google.co.ls/*
// @include *://www.google.lt/*
// @include *://www.google.lu/*
// @include *://www.google.lv/*
// @include *://www.google.com.ly/*
// @include *://www.google.co.ma/*
// @include *://www.google.md/*
// @include *://www.google.me/*
// @include *://www.google.mg/*
// @include *://www.google.mk/*
// @include *://www.google.ml/*
// @include *://www.google.mn/*
// @include *://www.google.ms/*
// @include *://www.google.com.mt/*
// @include *://www.google.mu/*
// @include *://www.google.mv/*
// @include *://www.google.mw/*
// @include *://www.google.com.mx/*
// @include *://www.google.com.my/*
// @include *://www.google.co.mz/*
// @include *://www.google.com.na/*
// @include *://www.google.com.nf/*
// @include *://www.google.com.ng/*
// @include *://www.google.com.ni/*
// @include *://www.google.ne/*
// @include *://www.google.nl/*
// @include *://www.google.no/*
// @include *://www.google.com.np/*
// @include *://www.google.nr/*
// @include *://www.google.nu/*
// @include *://www.google.co.nz/*
// @include *://www.google.com.om/*
// @include *://www.google.com.pa/*
// @include *://www.google.com.pe/*
// @include *://www.google.com.ph/*
// @include *://www.google.com.pk/*
// @include *://www.google.pl/*
// @include *://www.google.pn/*
// @include *://www.google.com.pr/*
// @include *://www.google.ps/*
// @include *://www.google.pt/*
// @include *://www.google.com.py/*
// @include *://www.google.com.qa/*
// @include *://www.google.ro/*
// @include *://www.google.ru/*
// @include *://www.google.rw/*
// @include *://www.google.com.sa/*
// @include *://www.google.com.sb/*
// @include *://www.google.sc/*
// @include *://www.google.se/*
// @include *://www.google.com.sg/*
// @include *://www.google.sh/*
// @include *://www.google.si/*
// @include *://www.google.sk/*
// @include *://www.google.com.sl/*
// @include *://www.google.sn/*
// @include *://www.google.sm/*
// @include *://www.google.st/*
// @include *://www.google.com.sv/*
// @include *://www.google.td/*
// @include *://www.google.tg/*
// @include *://www.google.co.th/*
// @include *://www.google.com.tj/*
// @include *://www.google.tk/*
// @include *://www.google.tl/*
// @include *://www.google.tm/*
// @include *://www.google.tn/*
// @include *://www.google.to/*
// @include *://www.google.com.tr/*
// @include *://www.google.tt/*
// @include *://www.google.com.tw/*
// @include *://www.google.co.tz/*
// @include *://www.google.com.ua/*
// @include *://www.google.co.ug/*
// @include *://www.google.co.uk/*
// @include *://www.google.com.uy/*
// @include *://www.google.co.uz/*
// @include *://www.google.com.vc/*
// @include *://www.google.co.ve/*
// @include *://www.google.vg/*
// @include *://www.google.co.vi/*
// @include *://www.google.com.vn/*
// @include *://www.google.vu/*
// @include *://www.google.ws/*
// @include *://www.google.rs/*
// @include *://www.google.co.za/*
// @include *://www.google.co.zm/*
// @include *://www.google.co.zw/*
// ==/UserScript==

var debug = false;

cLog("Start");

//
// Parses and puts in a hash map CGI URL parameters.
//
cLog("Window href:" + window.location.href);

//debugLayer = { 'FORM3' : 1, 'PROF' : 1 };
debugLayer = { 'INJ': 1 };

var vars = null;
function getUrlVars() {
    if (!vars) {
        vars = [];
        var hash;
        var positionQ = window.location.href.indexOf('?');
        var positionS = window.location.href.indexOf('#');

        if (positionQ > 0 && positionS > 0)
            position = Math.min(positionQ, positionS);
        else if (positionQ > 0)
            position = positionQ;
        else position = positionS;

        var hashes = window.location.href.slice(position + 1).split(/&|#/);

        for (var i = 0; i < hashes.length; i++) {
            cLog('VALUE:' + hashes[i]);
            hash = hashes[i].split('=');
            //vars.push(hash[0]);
            vars[hash[0]] = hash[1];

        }
        // Possibly missing hl value is filled with languages value corresponding to TLD value
        if (!vars['hl'])
            vars['hl'] = domainsToLang[getTLD()];
        // Possibly missing lr value is filled with languages value corresponding to TLD value
        if (!vars['lr'])
            vars['lr'] = "lang_" + domainsToLang[getTLD()];
    }
    return vars;
}

function getTLD() {
    var tld = window.location.href.match(/\.(\w+?)(\/|$)/)[1];
    cLog('TLD=' + tld);
    return tld;
}
cLog('TLD:' + getTLD());

//
// Main
//


//var addPath = "/html/body/div/*/div/div[2]/div";
//var addPath = "/html/body/div/div/div[2]/div[@id='resultStats']";
//var addPath = "/html/body/div[@id='main']/div[@id='cnt']/div[@id='subform_ctrl']";
//var addSelector = '#resultStats';
var addPaths = ["//*[@id='resultStats']", "//*[@id='gbx1']"];

//
// Add Possible Injection points below
//
// Places where to try injection of Language switcher box
// var addSelectors = { "#resultStats": 1, "#slim_appbar": 1, "#extabar": 1, "#topabar": 1, ".med": 1, ".g": 1, ".appbar": 1 };
var addSelectors = [ ["#rhs > div:first-of-type", "addBeforeElem", ""], ["#appbar", "addAfterElem", "float:right;"], ];
var languageVars = ["hl", "#hl"];
var documentFormName = 'gbqf';

/*
 *   Injected Code for querying as a different language
 */
var labels = ["en-US", "it", "fr", "de", "es", "cn/hk", "jp", "ru", "nl"];
//,"nl","ru","gr","jp","tr","cn","arb","il");

var languages = ["en", "it", "fr", "de", "es", "zh-CN", "ja", "ru", "nl"];
//,"nl","ru","el","ja","tr","zh-CN","ar","iw");

var domains = ["com", "it", "fr", "de", "es", "com.hk", "co.jp", "ru", "nl"];
var tlds = ["com", "it", "fr", "de", "es", "cn", "jp", "ru", "nl"];

var wikipediaDomains = ["en", "it", "fr", "de", "es", "zh", "ja", "ru", "nl"];

var domainsToLang = [];

for (var i = 0; i < domains.length; i++) {
    domainsToLang[tlds[i]] = languages[i];
}

var injectedObjectName = 'AGB_language_switcher';

var status = new Array();

var radioOn = "checked=\"checked\"";
var radioOff = "";

for (var i = 0; i < labels.length; i++) {
    if ("lang_" + languages[i] != getUrlVars()["lr"])
        status[languages[i]] = radioOff;
    else
        status[languages[i]] = radioOn;
}

cLog("GetURlVars=" + getUrlVars());
cLog("n_lang is=" + getUrlVars()["n_lang"]);
cLog("hl is=" + getUrlVars()["hl"]);
cLog("lr is=" + getUrlVars()["lr"]);
cLog("q is=" + getUrlVars()["q"]);
cLog("Languages[n_lang] is=" + status[getUrlVars()["hl"]]);


var inject = "<div id='" + injectedObjectName + "' class='q qs'><br>Ask again on another local version: \
<table  width='100%' border='0' cellpadding='0' cellspacing='0' style='position:static'><td> \
<table border='0' cellpadding='0' cellspacing='0'> \
<tbody><tr><th scope='row' align='left'>Google:</th>";

//
// Prepare code for radio buttons to be injected in Google page results
//

var googleInjectFuncMap = {};

for (let i = 0; i < languages.length; i++) {
    googleInjectFuncMap["label_g_" + languages[i]] = function() {
        var a = window.location.href.slice(0);
        a = a.replace(/google\.(.*?)\//, 'google.' + domains[i] + '/');
        if (!a.match(/(\?|\&)hl=/)) {
            a = a + '&hl=';
        }
        if (!a.match(/(\?|\&)lr=/)) {
            a = a + '&lr=';
        }
        a = a.replace(/hl=(.*?)(&|$)/, 'hl=' + languages[i] + '$2');
        a = a.replace(/lr=(.*?)(&|$)/, 'lr=lang_' + languages[i] + '$2');
        window.location.href = a;
    }

    //var clicInject = "document."+documentFormName+".hl.value ='"+languages[i]+"'; document."+documentFormName+".submit(); ";
    cLog("Injecting " + domains[i] + " code");

    var checked = ("lang_" + languages[i] == getUrlVars()["lr"]) && (tlds[i] == getTLD() || true);

    inject += "<td nowrap valign='middle'><label id='label_g_" + languages[i] + "' for=il \
        style=\"font-size:large;\"><input id='g_" + languages[i] + "' " + (checked ? ' checked ' : '') + "type=radio name=n_lang value='" + languages[i] + "'>" + labels[i] + "</label></td>";
}
inject += "<td>&nbsp;&nbsp;&nbsp;</td>";
cLog("This is what I'm injecting:" + inject);
/*
function getClicFunction(i)
{
    return function() { document[documentFormName].hl.value = languages[i]; document[documentFormName].submit(); };
}
for (var i = 0; i < languages.length; i++)
    {
        inject.append($('<td>').attr({ 'valign' : 'middle' })
                            .append($('<input>').attr( { id : 'g_'+languages[i], type : 'radio', name : 'n_lang', value : languages[i]}
                            )
                      ));
    }
*/
//
// Adds Wikipedia radio buttons. Constructs xx.wikipedia.org for xx taken from wikipediaDomains[]
//
inject += "</tr><tr><th scope='row' align='left'>Wikipedia:</th>";
var query = getUrlVars()["q"];
for (var i = 0; i < languages.length; i++) {
    var clicWikiInject = "window.location = 'http://" + wikipediaDomains[i] + ".wikipedia.org/?search=" + query + "';";

    inject += "<td nowrap valign='middle'><input id=g_w" + languages[i] + " type=radio name=n_lang value='w" + languages[i] + "' \
                             onClick=\""+ clicWikiInject + "\"><label for=il>" + labels[i] + "</label></td>";

}
inject += "<td>&nbsp;&nbsp;&nbsp;</td></tr></tbody></table></td></table>";

tryInjection();
document.addEventListener("DOMNodeInserted", function (event) {
    var element = event.target;
    //cLog("Node INSERTED "+element.name);
    //if (element.className == 'med') {
    tryInjection();
    //}
});

var alreadyInjected = false;
function tryInjection() {
    if (alreadyInjected) return;
    var placeToInject = null;
    if (!document.getElementById(injectedObjectName)) {
        lLog("Starting injection", "INJ");

        var selector;
        var insertMethod;
        var extraStyles;
        for (var pair of addSelectors) {
            selector = pair[0];
            insertMethod = pair[1];
            extraStyles = pair[2];
            lLog("Attempting injection point: " + selector, "INJ");
            if (placeToInject = document.querySelectorAll(selector)[0]) {
                lLog("Matched Selector:" + selector, "INJ"); break;
            }
        }
        lLog("Place to inject:" + placeToInject, "INJ");
        if (placeToInject) {
            //placeToInject.innerHTML = "<td>"+inject+"</td></div>" + placeToInject.innerHTML;
            // document.querySelectorAll("#appbar").forEach(el => { el.style.zIndex = 999 });
            document.querySelectorAll(selector).forEach(el => {
                var mainElem = new DOMParser().parseFromString(inject, "text/html").body.firstChild;
                if (insertMethod === "addBeforeElem") {
                    el.before(mainElem);
                } else {
                    el.after(mainElem);
                }
                mainElem.setAttribute("style", extraStyles);
            });
            for (var labelId in googleInjectFuncMap) {
                document.getElementById(labelId).onclick = googleInjectFuncMap[labelId];
            }
            alreadyInjected = true;
        }
        lLog("Ending injection", "INJ");

    }
    else lLog("Looks like there is already a language_switcher here.", "INJ2");
}

function cLog(str) {
    if (debug) console.log(str);
}

/**
 * main logging function
 * @param str  String to be printed
 * @param level debugLayer triggering the printout. Will be printed only if level is in the currently active debugLayers
 */

function lLog(str, level) {
    //console.log("Logging:"+str);
    if (!isNaN(level))
        str += " USING NUMBERS FOR DEBUG LEVELS IS DEPRECATED - ";
    else if (level.match(/PROF/)) {
        str = this.debug.getPace() + "\n" + str;
    };
    //	console.log("mid Logging:"+this.debugLayer+" "+this.localLog);

    if (debugLayer == level || debugLayer == "ALL" || (debugLayer instanceof Object && debugLayer[level])) { cLog(str); }
    //console.log("end Logging:"+str);

};

//
// XPath evaluation wrapping function.
//
function xpath(query, node) {
    return document.evaluate(query, node, null,
        XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
}

//
// Returns the first element of a given XPath query
//
function fXPath(query, node) {
    var elements = xpath(query, node);
    return elements.snapshotItem(0);
}

//
// Returns the last element of a given XPath query
//
function lXPath(query, node) {
    cLog("Asking for " + query);
    var elements = xpath(query, node);
    cLog("Elements are " + elements.snapshotLength);
    return elements.snapshotItem(elements.snapshotLength - 1);
}

//
// 0-ize NaN and non-number strings
//
function normalize(str) {
    var temp = (str != null) ? parseInt(str) : 0;
    return (isNaN(temp)) ? 0 : temp;
}