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

  1. // ==UserScript==
  2. // @name Language Switcher for Google™
  3. // @author gb.ianni shunf4
  4. // @namespace gb.ianni
  5. // @version 2.3.4
  6. // @copyright Copyright 2015-2022 gb.ianni shunf4
  7. // @description a port of Language Switcher for Google™ Chome extension (https://chrome.google.com/webstore/detail/language-switcher-for-goo/jbbappjmafnnhhalfbhdhiemchcgejcp) to userscript.js
  8. // @include *://www.google.com/*
  9. // @include *://www.google.ad/*
  10. // @include *://www.google.ae/*
  11. // @include *://www.google.com.af/*
  12. // @include *://www.google.com.ag/*
  13. // @include *://www.google.com.ai/*
  14. // @include *://www.google.am/*
  15. // @include *://www.google.co.ao/*
  16. // @include *://www.google.com.ar/*
  17. // @include *://www.google.as/*
  18. // @include *://www.google.at/*
  19. // @include *://www.google.com.au/*
  20. // @include *://www.google.az/*
  21. // @include *://www.google.ba/*
  22. // @include *://www.google.com.bd/*
  23. // @include *://www.google.be/*
  24. // @include *://www.google.bf/*
  25. // @include *://www.google.bg/*
  26. // @include *://www.google.com.bh/*
  27. // @include *://www.google.bi/*
  28. // @include *://www.google.bj/*
  29. // @include *://www.google.com.bn/*
  30. // @include *://www.google.com.bo/*
  31. // @include *://www.google.com.br/*
  32. // @include *://www.google.bs/*
  33. // @include *://www.google.co.bw/*
  34. // @include *://www.google.by/*
  35. // @include *://www.google.com.bz/*
  36. // @include *://www.google.ca/*
  37. // @include *://www.google.cd/*
  38. // @include *://www.google.cf/*
  39. // @include *://www.google.cg/*
  40. // @include *://www.google.ch/*
  41. // @include *://www.google.ci/*
  42. // @include *://www.google.co.ck/*
  43. // @include *://www.google.cl/*
  44. // @include *://www.google.cm/*
  45. // @include *://www.google.cn/*
  46. // @include *://www.google.com.co/*
  47. // @include *://www.google.co.cr/*
  48. // @include *://www.google.com.cu/*
  49. // @include *://www.google.cz/*
  50. // @include *://www.google.de/*
  51. // @include *://www.google.dj/*
  52. // @include *://www.google.dk/*
  53. // @include *://www.google.dm/*
  54. // @include *://www.google.com.do/*
  55. // @include *://www.google.dz/*
  56. // @include *://www.google.com.ec/*
  57. // @include *://www.google.ee/*
  58. // @include *://www.google.com.eg/*
  59. // @include *://www.google.es/*
  60. // @include *://www.google.com.et/*
  61. // @include *://www.google.fi/*
  62. // @include *://www.google.com.fj/*
  63. // @include *://www.google.fm/*
  64. // @include *://www.google.fr/*
  65. // @include *://www.google.ga/*
  66. // @include *://www.google.ge/*
  67. // @include *://www.google.gg/*
  68. // @include *://www.google.com.gh/*
  69. // @include *://www.google.com.gi/*
  70. // @include *://www.google.gl/*
  71. // @include *://www.google.gm/*
  72. // @include *://www.google.gp/*
  73. // @include *://www.google.gr/*
  74. // @include *://www.google.com.gt/*
  75. // @include *://www.google.gy/*
  76. // @include *://www.google.com.hk/*
  77. // @include *://www.google.hn/*
  78. // @include *://www.google.hr/*
  79. // @include *://www.google.ht/*
  80. // @include *://www.google.hu/*
  81. // @include *://www.google.co.id/*
  82. // @include *://www.google.ie/*
  83. // @include *://www.google.co.il/*
  84. // @include *://www.google.im/*
  85. // @include *://www.google.co.in/*
  86. // @include *://www.google.iq/*
  87. // @include *://www.google.is/*
  88. // @include *://www.google.it/*
  89. // @include *://www.google.je/*
  90. // @include *://www.google.com.jm/*
  91. // @include *://www.google.jo/*
  92. // @include *://www.google.co.jp/*
  93. // @include *://www.google.co.ke/*
  94. // @include *://www.google.com.kh/*
  95. // @include *://www.google.ki/*
  96. // @include *://www.google.kg/*
  97. // @include *://www.google.co.kr/*
  98. // @include *://www.google.com.kw/*
  99. // @include *://www.google.kz/*
  100. // @include *://www.google.la/*
  101. // @include *://www.google.com.lb/*
  102. // @include *://www.google.li/*
  103. // @include *://www.google.lk/*
  104. // @include *://www.google.co.ls/*
  105. // @include *://www.google.lt/*
  106. // @include *://www.google.lu/*
  107. // @include *://www.google.lv/*
  108. // @include *://www.google.com.ly/*
  109. // @include *://www.google.co.ma/*
  110. // @include *://www.google.md/*
  111. // @include *://www.google.me/*
  112. // @include *://www.google.mg/*
  113. // @include *://www.google.mk/*
  114. // @include *://www.google.ml/*
  115. // @include *://www.google.mn/*
  116. // @include *://www.google.ms/*
  117. // @include *://www.google.com.mt/*
  118. // @include *://www.google.mu/*
  119. // @include *://www.google.mv/*
  120. // @include *://www.google.mw/*
  121. // @include *://www.google.com.mx/*
  122. // @include *://www.google.com.my/*
  123. // @include *://www.google.co.mz/*
  124. // @include *://www.google.com.na/*
  125. // @include *://www.google.com.nf/*
  126. // @include *://www.google.com.ng/*
  127. // @include *://www.google.com.ni/*
  128. // @include *://www.google.ne/*
  129. // @include *://www.google.nl/*
  130. // @include *://www.google.no/*
  131. // @include *://www.google.com.np/*
  132. // @include *://www.google.nr/*
  133. // @include *://www.google.nu/*
  134. // @include *://www.google.co.nz/*
  135. // @include *://www.google.com.om/*
  136. // @include *://www.google.com.pa/*
  137. // @include *://www.google.com.pe/*
  138. // @include *://www.google.com.ph/*
  139. // @include *://www.google.com.pk/*
  140. // @include *://www.google.pl/*
  141. // @include *://www.google.pn/*
  142. // @include *://www.google.com.pr/*
  143. // @include *://www.google.ps/*
  144. // @include *://www.google.pt/*
  145. // @include *://www.google.com.py/*
  146. // @include *://www.google.com.qa/*
  147. // @include *://www.google.ro/*
  148. // @include *://www.google.ru/*
  149. // @include *://www.google.rw/*
  150. // @include *://www.google.com.sa/*
  151. // @include *://www.google.com.sb/*
  152. // @include *://www.google.sc/*
  153. // @include *://www.google.se/*
  154. // @include *://www.google.com.sg/*
  155. // @include *://www.google.sh/*
  156. // @include *://www.google.si/*
  157. // @include *://www.google.sk/*
  158. // @include *://www.google.com.sl/*
  159. // @include *://www.google.sn/*
  160. // @include *://www.google.sm/*
  161. // @include *://www.google.st/*
  162. // @include *://www.google.com.sv/*
  163. // @include *://www.google.td/*
  164. // @include *://www.google.tg/*
  165. // @include *://www.google.co.th/*
  166. // @include *://www.google.com.tj/*
  167. // @include *://www.google.tk/*
  168. // @include *://www.google.tl/*
  169. // @include *://www.google.tm/*
  170. // @include *://www.google.tn/*
  171. // @include *://www.google.to/*
  172. // @include *://www.google.com.tr/*
  173. // @include *://www.google.tt/*
  174. // @include *://www.google.com.tw/*
  175. // @include *://www.google.co.tz/*
  176. // @include *://www.google.com.ua/*
  177. // @include *://www.google.co.ug/*
  178. // @include *://www.google.co.uk/*
  179. // @include *://www.google.com.uy/*
  180. // @include *://www.google.co.uz/*
  181. // @include *://www.google.com.vc/*
  182. // @include *://www.google.co.ve/*
  183. // @include *://www.google.vg/*
  184. // @include *://www.google.co.vi/*
  185. // @include *://www.google.com.vn/*
  186. // @include *://www.google.vu/*
  187. // @include *://www.google.ws/*
  188. // @include *://www.google.rs/*
  189. // @include *://www.google.co.za/*
  190. // @include *://www.google.co.zm/*
  191. // @include *://www.google.co.zw/*
  192. // ==/UserScript==
  193.  
  194. var debug = false;
  195.  
  196. cLog("Start");
  197.  
  198. //
  199. // Parses and puts in a hash map CGI URL parameters.
  200. //
  201. cLog("Window href:" + window.location.href);
  202.  
  203. //debugLayer = { 'FORM3' : 1, 'PROF' : 1 };
  204. debugLayer = { 'INJ': 1 };
  205.  
  206. var vars = null;
  207. function getUrlVars() {
  208. if (!vars) {
  209. vars = [];
  210. var hash;
  211. var positionQ = window.location.href.indexOf('?');
  212. var positionS = window.location.href.indexOf('#');
  213.  
  214. if (positionQ > 0 && positionS > 0)
  215. position = Math.min(positionQ, positionS);
  216. else if (positionQ > 0)
  217. position = positionQ;
  218. else position = positionS;
  219.  
  220. var hashes = window.location.href.slice(position + 1).split(/&|#/);
  221.  
  222. for (var i = 0; i < hashes.length; i++) {
  223. cLog('VALUE:' + hashes[i]);
  224. hash = hashes[i].split('=');
  225. //vars.push(hash[0]);
  226. vars[hash[0]] = hash[1];
  227.  
  228. }
  229. // Possibly missing hl value is filled with languages value corresponding to TLD value
  230. if (!vars['hl'])
  231. vars['hl'] = domainsToLang[getTLD()];
  232. // Possibly missing lr value is filled with languages value corresponding to TLD value
  233. if (!vars['lr'])
  234. vars['lr'] = "lang_" + domainsToLang[getTLD()];
  235. }
  236. return vars;
  237. }
  238.  
  239. function getTLD() {
  240. var tld = window.location.href.match(/\.(\w+?)(\/|$)/)[1];
  241. cLog('TLD=' + tld);
  242. return tld;
  243. }
  244. cLog('TLD:' + getTLD());
  245.  
  246. //
  247. // Main
  248. //
  249.  
  250.  
  251. //var addPath = "/html/body/div/*/div/div[2]/div";
  252. //var addPath = "/html/body/div/div/div[2]/div[@id='resultStats']";
  253. //var addPath = "/html/body/div[@id='main']/div[@id='cnt']/div[@id='subform_ctrl']";
  254. //var addSelector = '#resultStats';
  255. var addPaths = ["//*[@id='resultStats']", "//*[@id='gbx1']"];
  256.  
  257. //
  258. // Add Possible Injection points below
  259. //
  260. // Places where to try injection of Language switcher box
  261. // var addSelectors = { "#resultStats": 1, "#slim_appbar": 1, "#extabar": 1, "#topabar": 1, ".med": 1, ".g": 1, ".appbar": 1 };
  262. var addSelectors = [ ["#rhs > div:first-of-type", "addBeforeElem", ""], ["#appbar", "addAfterElem", "float:right;"], ];
  263. var languageVars = ["hl", "#hl"];
  264. var documentFormName = 'gbqf';
  265.  
  266. /*
  267. * Injected Code for querying as a different language
  268. */
  269. var labels = ["en-US", "it", "fr", "de", "es", "cn/hk", "jp", "ru", "nl"];
  270. //,"nl","ru","gr","jp","tr","cn","arb","il");
  271.  
  272. var languages = ["en", "it", "fr", "de", "es", "zh-CN", "ja", "ru", "nl"];
  273. //,"nl","ru","el","ja","tr","zh-CN","ar","iw");
  274.  
  275. var domains = ["com", "it", "fr", "de", "es", "com.hk", "co.jp", "ru", "nl"];
  276. var tlds = ["com", "it", "fr", "de", "es", "cn", "jp", "ru", "nl"];
  277.  
  278. var wikipediaDomains = ["en", "it", "fr", "de", "es", "zh", "ja", "ru", "nl"];
  279.  
  280. var domainsToLang = [];
  281.  
  282. for (var i = 0; i < domains.length; i++) {
  283. domainsToLang[tlds[i]] = languages[i];
  284. }
  285.  
  286. var injectedObjectName = 'AGB_language_switcher';
  287.  
  288. var status = new Array();
  289.  
  290. var radioOn = "checked=\"checked\"";
  291. var radioOff = "";
  292.  
  293. for (var i = 0; i < labels.length; i++) {
  294. if ("lang_" + languages[i] != getUrlVars()["lr"])
  295. status[languages[i]] = radioOff;
  296. else
  297. status[languages[i]] = radioOn;
  298. }
  299.  
  300. cLog("GetURlVars=" + getUrlVars());
  301. cLog("n_lang is=" + getUrlVars()["n_lang"]);
  302. cLog("hl is=" + getUrlVars()["hl"]);
  303. cLog("lr is=" + getUrlVars()["lr"]);
  304. cLog("q is=" + getUrlVars()["q"]);
  305. cLog("Languages[n_lang] is=" + status[getUrlVars()["hl"]]);
  306.  
  307.  
  308. var inject = "<div id='" + injectedObjectName + "' class='q qs'><br>Ask again on another local version: \
  309. <table width='100%' border='0' cellpadding='0' cellspacing='0' style='position:static'><td> \
  310. <table border='0' cellpadding='0' cellspacing='0'> \
  311. <tbody><tr><th scope='row' align='left'>Google:</th>";
  312.  
  313. //
  314. // Prepare code for radio buttons to be injected in Google page results
  315. //
  316.  
  317. var googleInjectFuncMap = {};
  318.  
  319. for (let i = 0; i < languages.length; i++) {
  320. googleInjectFuncMap["label_g_" + languages[i]] = function() {
  321. var a = window.location.href.slice(0);
  322. a = a.replace(/google\.(.*?)\//, 'google.' + domains[i] + '/');
  323. if (!a.match(/(\?|\&)hl=/)) {
  324. a = a + '&hl=';
  325. }
  326. if (!a.match(/(\?|\&)lr=/)) {
  327. a = a + '&lr=';
  328. }
  329. a = a.replace(/hl=(.*?)(&|$)/, 'hl=' + languages[i] + '$2');
  330. a = a.replace(/lr=(.*?)(&|$)/, 'lr=lang_' + languages[i] + '$2');
  331. window.location.href = a;
  332. }
  333.  
  334. //var clicInject = "document."+documentFormName+".hl.value ='"+languages[i]+"'; document."+documentFormName+".submit(); ";
  335. cLog("Injecting " + domains[i] + " code");
  336.  
  337. var checked = ("lang_" + languages[i] == getUrlVars()["lr"]) && (tlds[i] == getTLD() || true);
  338.  
  339. inject += "<td nowrap valign='middle'><label id='label_g_" + languages[i] + "' for=il \
  340. style=\"font-size:large;\"><input id='g_" + languages[i] + "' " + (checked ? ' checked ' : '') + "type=radio name=n_lang value='" + languages[i] + "'>" + labels[i] + "</label></td>";
  341. }
  342. inject += "<td>&nbsp;&nbsp;&nbsp;</td>";
  343. cLog("This is what I'm injecting:" + inject);
  344. /*
  345. function getClicFunction(i)
  346. {
  347. return function() { document[documentFormName].hl.value = languages[i]; document[documentFormName].submit(); };
  348. }
  349. for (var i = 0; i < languages.length; i++)
  350. {
  351. inject.append($('<td>').attr({ 'valign' : 'middle' })
  352. .append($('<input>').attr( { id : 'g_'+languages[i], type : 'radio', name : 'n_lang', value : languages[i]}
  353. )
  354. ));
  355. }
  356. */
  357. //
  358. // Adds Wikipedia radio buttons. Constructs xx.wikipedia.org for xx taken from wikipediaDomains[]
  359. //
  360. inject += "</tr><tr><th scope='row' align='left'>Wikipedia:</th>";
  361. var query = getUrlVars()["q"];
  362. for (var i = 0; i < languages.length; i++) {
  363. var clicWikiInject = "window.location = 'http://" + wikipediaDomains[i] + ".wikipedia.org/?search=" + query + "';";
  364.  
  365. inject += "<td nowrap valign='middle'><input id=g_w" + languages[i] + " type=radio name=n_lang value='w" + languages[i] + "' \
  366. onClick=\""+ clicWikiInject + "\"><label for=il>" + labels[i] + "</label></td>";
  367.  
  368. }
  369. inject += "<td>&nbsp;&nbsp;&nbsp;</td></tr></tbody></table></td></table>";
  370.  
  371. tryInjection();
  372. document.addEventListener("DOMNodeInserted", function (event) {
  373. var element = event.target;
  374. //cLog("Node INSERTED "+element.name);
  375. //if (element.className == 'med') {
  376. tryInjection();
  377. //}
  378. });
  379.  
  380. var alreadyInjected = false;
  381. function tryInjection() {
  382. if (alreadyInjected) return;
  383. var placeToInject = null;
  384. if (!document.getElementById(injectedObjectName)) {
  385. lLog("Starting injection", "INJ");
  386.  
  387. var selector;
  388. var insertMethod;
  389. var extraStyles;
  390. for (var pair of addSelectors) {
  391. selector = pair[0];
  392. insertMethod = pair[1];
  393. extraStyles = pair[2];
  394. lLog("Attempting injection point: " + selector, "INJ");
  395. if (placeToInject = document.querySelectorAll(selector)[0]) {
  396. lLog("Matched Selector:" + selector, "INJ"); break;
  397. }
  398. }
  399. lLog("Place to inject:" + placeToInject, "INJ");
  400. if (placeToInject) {
  401. //placeToInject.innerHTML = "<td>"+inject+"</td></div>" + placeToInject.innerHTML;
  402. // document.querySelectorAll("#appbar").forEach(el => { el.style.zIndex = 999 });
  403. document.querySelectorAll(selector).forEach(el => {
  404. var mainElem = new DOMParser().parseFromString(inject, "text/html").body.firstChild;
  405. if (insertMethod === "addBeforeElem") {
  406. el.before(mainElem);
  407. } else {
  408. el.after(mainElem);
  409. }
  410. mainElem.setAttribute("style", extraStyles);
  411. });
  412. for (var labelId in googleInjectFuncMap) {
  413. document.getElementById(labelId).onclick = googleInjectFuncMap[labelId];
  414. }
  415. alreadyInjected = true;
  416. }
  417. lLog("Ending injection", "INJ");
  418.  
  419. }
  420. else lLog("Looks like there is already a language_switcher here.", "INJ2");
  421. }
  422.  
  423. function cLog(str) {
  424. if (debug) console.log(str);
  425. }
  426.  
  427. /**
  428. * main logging function
  429. * @param str String to be printed
  430. * @param level debugLayer triggering the printout. Will be printed only if level is in the currently active debugLayers
  431. */
  432.  
  433. function lLog(str, level) {
  434. //console.log("Logging:"+str);
  435. if (!isNaN(level))
  436. str += " USING NUMBERS FOR DEBUG LEVELS IS DEPRECATED - ";
  437. else if (level.match(/PROF/)) {
  438. str = this.debug.getPace() + "\n" + str;
  439. };
  440. // console.log("mid Logging:"+this.debugLayer+" "+this.localLog);
  441.  
  442. if (debugLayer == level || debugLayer == "ALL" || (debugLayer instanceof Object && debugLayer[level])) { cLog(str); }
  443. //console.log("end Logging:"+str);
  444.  
  445. };
  446.  
  447. //
  448. // XPath evaluation wrapping function.
  449. //
  450. function xpath(query, node) {
  451. return document.evaluate(query, node, null,
  452. XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  453. }
  454.  
  455. //
  456. // Returns the first element of a given XPath query
  457. //
  458. function fXPath(query, node) {
  459. var elements = xpath(query, node);
  460. return elements.snapshotItem(0);
  461. }
  462.  
  463. //
  464. // Returns the last element of a given XPath query
  465. //
  466. function lXPath(query, node) {
  467. cLog("Asking for " + query);
  468. var elements = xpath(query, node);
  469. cLog("Elements are " + elements.snapshotLength);
  470. return elements.snapshotItem(elements.snapshotLength - 1);
  471. }
  472.  
  473. //
  474. // 0-ize NaN and non-number strings
  475. //
  476. function normalize(str) {
  477. var temp = (str != null) ? parseInt(str) : 0;
  478. return (isNaN(temp)) ? 0 : temp;
  479. }