Wikipedia multi language view

View a Wikipedia entry with two (or more?) languages side by side for comparison and language learning.

  1. // ==UserScript==
  2. // @name Wikipedia multi language view
  3. // @name:en Wikipedia multi language view
  4. // @name:zh Wikipedia 多语言浏览
  5. // @name:ja ウィキペディアの多言語表示
  6. // @name:de Wikipedia Mehrsprachige Ansicht
  7. // @name:fr Vue multilingue de Wikipédia
  8. // @name:es Vista multilingüe de Wikipedia
  9. // @name:ru Многоязычный вид Википедии
  10. // @name:it Visualizzazione multilingue di Wikipedia
  11. // @name:ko 위키백과 다국어 보기
  12. // @name:pt Visualização multilíngue da Wikipédia
  13. // @name:ar عرض ويكيبيديا متعدد اللغات
  14. // @name:vi Chế độ xem đa ngôn ngữ của Wikipedia
  15. // @name:pl Wielojęzyczny widok Wikipedii
  16. // @name:uk Багатомовний перегляд Вікіпедії
  17. // @name:nl Meertalige weergave van Wikipedia
  18. // @name:sv Flerspråkig vy av Wikipedia
  19. // @name:id Tampilan multibahasa Wikipedia
  20. // @name:fi Monikielinen Wikipedia-näkymä
  21. // @name:no Flerspråklig visning av Wikipedia
  22. // @namespace https://userscript.snomiao.com/
  23. // @author snomiao@gmail.com
  24. // @version 0.0.8
  25. // @description View a Wikipedia entry with two (or more?) languages side by side for comparison and language learning.
  26. // @description:zh 以并列多语言视角浏览维基百科
  27. // @description:ja 比較と語学学習のために、ウィキペディアの記事を2つ(またはそれ以上?)の言語で並べて表示します。
  28. // @description:de Vergleichen und Sprachen lernen: Wikipedia-Einträge in zwei (oder mehr?) Sprachen nebeneinander anzeigen.
  29. // @description:fr Affichez un article de Wikipédia dans deux (ou plusieurs ?) langues côte à côte pour la comparaison et l'apprentissage des langues.
  30. // @description:es Ver una entrada de Wikipedia en dos (¿o más?) idiomas lado a lado para comparar y aprender idiomas.
  31. // @description:ru Просмотр статьи Википедии на двух (или более?) языках рядом для сравнения и изучения языков.
  32. // @description:it Visualizza un articolo di Wikipedia in due (o più?) lingue affiancate per il confronto e l'apprendimento delle lingue.
  33. // @description:ko 비교와 언어 학습을 위해 위키백과 항목을 두 개(또는 그 이상?)의 언어로 나란히 표시합니다.
  34. // @description:pt Visualize uma entrada da Wikipédia em dois (ou mais?) idiomas lado a lado para comparação e aprendizado de idiomas.
  35. // @description:ar عرض مقالة ويكيبيديا بلغة (أو أكثر؟) جنبًا إلى جنب للمقارنة وتعلم اللغات.
  36. // @description:vi Xem một bài viết trên Wikipedia bằng hai (hoặc nhiều hơn?) ngôn ngữ song song để so sánh và học ngôn ngữ.
  37. // @description:pl Wyświetl wpis Wikipedii w dwóch (lub więcej?) językach obok siebie w celu porównania i nauki języków.
  38. // @description:uk Перегляд статті Вікіпедії двома (або більше?) мовами поруч для порівняння та вивчення мов.
  39. // @description:nl Bekijk een Wikipedia-artikel in twee (of meer?) talen naast elkaar voor vergelijking en taalleren.
  40. // @description:sv Visa en Wikipedia-post på två (eller fler?) språk sida vid sida för jämförelse och språkinlärning.
  41. // @description:id Lihat entri Wikipedia dalam dua (atau lebih?) bahasa berdampingan untuk perbandingan dan pembelajaran bahasa.
  42. // @description:fi Näytä Wikipedia-artikkeli kahdella (tai useammalla?) kielellä rinnakkain vertailua ja kielten oppimista varten.
  43. // @description:no Vis en Wikipedia-artikkel på to (eller flere?) språk side om side for sammenligning og språklæring.
  44. // @match https://*.wikipedia.org/wiki/*
  45. // @match https://zh.wikipedia.org/zh-*/*
  46. // @grant none
  47. // @run-at document-start
  48. // @license MIT
  49. // @supportURL https://github.com/snomiao/multilang-wiki/issues
  50. // @contributionURL https://snomiao.com/donate
  51. // ==/UserScript==
  52. //
  53. // ref:
  54. // @downloadURL-old https://raw.githubusercontent.com/snomiao/userscript.js/main/src/wiki-multilang.user.js
  55. // [javascript - Resize Cross Domain Iframe Height - Stack Overflow]( https://stackoverflow.com/questions/22086722/resize-cross-domain-iframe-height )
  56. //
  57. // 2025-05-29 udpate: add more languages, support more than 2 columns
  58.  
  59. // const Langs = ['en', 'ja', 'zh', 'de', 'fr', 'es', 'ru', 'it', 'ko', 'pt', 'ar', 'vi', 'pl', 'uk', 'nl', 'sv', 'id', 'fi', 'no', 'tr', 'cs', 'da', 'he', 'hu', 'ro', 'th']
  60. const langs = ["en", "fr", "ja", "zh"]; // modify this to your preferred languages, will be used to load the 2nd language iframe
  61.  
  62. function main() {
  63. // hide sidebars
  64. setTimeout(() => {
  65. [
  66. ...document.body.querySelectorAll(
  67. "#sidebarCollapse,.vector-pinnable-header-unpin-button"
  68. ),
  69. ]
  70. .filter((e) => e?.checkVisibility?.())
  71. .map((e) => e?.click?.());
  72. }, 1e3);
  73. }
  74. main();
  75.  
  76. if (location.hash.match("#langIfr")) {
  77. // iframe code send height
  78. const sendHeight = () =>
  79. parent.postMessage?.(
  80. {
  81. langIfr: {
  82. height: document.body.scrollHeight,
  83. lang: location.hash.match?.(/#langIfr-(..)/)?.[1],
  84. },
  85. },
  86. "*"
  87. );
  88. window.addEventListener("resize", sendHeight, false);
  89. window.addEventListener("load", sendHeight, false);
  90.  
  91. sendHeight();
  92. document.head.appendChild(createHtmlElement('<base target="_parent" />'));
  93. } else {
  94. // parent code recv iframe's height
  95. const msgHandler = (e) => {
  96. const setHeight = ({ height, lang }) =>
  97. height &&
  98. lang &&
  99. document
  100. .querySelector?.(`.langIfr[lang=${lang}]`)
  101. ?.setAttribute("height", height);
  102. setHeight(e.data?.langIfr);
  103. };
  104. window.addEventListener("message", msgHandler, false);
  105. // load iframe
  106. const langLnksGet = () =>
  107. Object.fromEntries(
  108. [...document.querySelectorAll("a.interlanguage-link-target")]
  109. .map((e) => ({
  110. lang: e.getAttribute("lang"),
  111. href: e.href,
  112. language: e.textContent,
  113. }))
  114. .map((e) => [e.lang, e])
  115. );
  116. const columns = (document.body.clientWidth / 800) | 0;
  117. console.log("multilang-wiki: " + columns + " columns");
  118. const exlangFrameLoad = () => {
  119. const langLnks = langLnksGet();
  120. const avaliableLangs = langs
  121. .filter((lang) => langLnks[lang])
  122. .filter((lang, i) => i < columns);
  123. console.log("Avaliable languages: " + avaliableLangs);
  124.  
  125. const width = (100 / (avaliableLangs.length + 1)).toFixed(2) + "vw";
  126. const langIframeLoad = (lang = "en") => {
  127. if (!langLnks[lang]) return null;
  128. const count = [...document.querySelectorAll(".langIfr")].length;
  129. document.body.setAttribute(
  130. "style",
  131. `width: ${width}; overflow-x: hidden`
  132. );
  133. document.body.querySelector(`.langIfr[lang=${lang}]`)?.remove();
  134. const langIfr = createHtmlElement(`
  135. <iframe
  136. class="langIfr"
  137. lang="${lang}"
  138. src="${langLnks[lang].href + "#langIfr-" + lang}"
  139. style="border: none; position:absolute; left: calc(${
  140. 1 + count
  141. } * ${width}); top: 0vh; width: ${width}; min-height: 100vh"
  142. allow="fullscreen"
  143. ></iframe>`);
  144. document.body.appendChild(langIfr);
  145. return langIfr;
  146. };
  147.  
  148. // the load 2st language for current page
  149.  
  150. avaliableLangs.forEach((lang) => langIframeLoad(lang));
  151.  
  152. // langs.find(lang => langIframeLoad(lang))
  153. };
  154. window.addEventListener("load", exlangFrameLoad, false);
  155. }
  156.  
  157. function createHtmlElement(innerHTML = "<span>hello</span>") {
  158. return Object.assign(document.createElement("div"), { innerHTML })
  159. .children[0];
  160. }