Translate Discord Messages

Translate Discord messages and display them under the original text

当前为 2024-03-09 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Translate Discord Messages
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.1.0
  5. // @description Translate Discord messages and display them under the original text
  6. // @author CrankyBookworm
  7. // @match https://*.discord.com/*
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=discord.com
  9. // @grant GM.xmlHttpRequest
  10. // @grant GM_xmlhttpRequest
  11. // @connect translate.googleapis.com
  12. // @esversion 8
  13. // @run-at document-end
  14. // @license MIT
  15. // ==/UserScript==
  16.  
  17. /* jshint esversion: 11 */
  18.  
  19. const translateTo = "en";
  20.  
  21. let httpRequest;
  22. if (typeof GM < "u" && GM.xmlHttpRequest) {
  23. httpRequest = GM.xmlHttpRequest;
  24. }
  25. else if (typeof GM < "u" && GM_xmlhttpRequest) {
  26. httpRequest = GM_xmlhttpRequest;
  27. }
  28. else if (typeof GM_xmlhttpRequest < "u") {
  29. httpRequest = GM_xmlhttpRequest;
  30. }
  31. else if (typeof GM < "u" && GM.xmlHttpRequest) {
  32. httpRequest = GM.xmlHttpRequest;
  33. }
  34. else {
  35. httpRequest = fetch;
  36. console.error("No HttpRequest Permission");
  37. }
  38.  
  39. class TranslationResult {
  40. constructor() {
  41. /**
  42. * Translated Text.
  43. * @var {string}
  44. */
  45. this.resultText = "";
  46. /**
  47. * Alternative translation results.
  48. * @var {string}
  49. */
  50. this.candidateText = "";
  51. /**
  52. * Translated from.
  53. * @var {string}
  54. */
  55. this.sourceLanguage = "";
  56. /**
  57. * Translation accuracy.
  58. * @var {float}
  59. */
  60. this.percentage = 0;
  61. /**
  62. * Is Error.
  63. * @var {bool}
  64. */
  65. this.isError = !1;
  66. /**
  67. * Error Message.
  68. * @var {string}
  69. */
  70. this.errorMessage = "";
  71. }
  72. }
  73.  
  74. (function () {
  75. 'use strict';
  76.  
  77. const horizontalLine = document.createElement("hr");
  78. horizontalLine.style = "border: 0; padding-top: 1px; background: linear-gradient(to right, transparent, #d0d0d5, transparent);";
  79.  
  80. const translateMsgElementTemplate = document.createElement("span");
  81. translateMsgElementTemplate.className = "myTranslateMsg";
  82. translateMsgElementTemplate.style = "color:#a4a9ab;";
  83. translateMsgElementTemplate.lang = translateTo;
  84. translateMsgElementTemplate.innerText = "Testing";
  85.  
  86.  
  87. /**
  88. * Translates a message
  89. * @param {string} textToTranslate Text that needs to be translated
  90. * @param {string?} fromLang Language to translate from
  91. * @param {string?} toLang Language to translate to
  92. * @returns {TranslationResult} Translation Result
  93. */
  94. async function translateText(textToTranslate, fromLang, toLang) {
  95. fromLang = fromLang ?? "auto";
  96. toLang = toLang ?? translateTo;
  97. const url = `https://translate.googleapis.com/translate_a/single?client=gtx&sl=${fromLang}&tl=${toLang}&dt=t&dt=bd&dj=1&q=${encodeURIComponent(textToTranslate)}`;
  98.  
  99. var request = {
  100. "method": "GET",
  101. "timeout" : 6e4,
  102. "url": url,
  103. "synchronous" : !1,
  104. };
  105.  
  106. /** @type {Response} */
  107. const response = await httpRequest(request).catch((e => e.response));
  108. const json_data = JSON.parse(await response.responseText);
  109. var result = new TranslationResult();
  110.  
  111. // console.log(`response: Type: ${typeof response}. Value: ${response}`);
  112. if (response && 200 === (null == response ? void 0 : response.status)) {
  113. console.log(`json_data: Type: ${typeof json_data}. Value: ${json_data}`);
  114. result.sourceLanguage = json_data.src;
  115. result.percentage = json_data.ld_result.srclangs_confidences[0];
  116. result.resultText = json_data.sentences.map((e => e.trans)).join("");
  117. if(json_data.dict){
  118. result.candidateText = json_data.dict.map(
  119. (e => `${e.pos}${"" != e.pos ? ": " : ""}${e.terms.join(", ")}\n`).join("")
  120. );
  121. }
  122. }
  123. else {
  124. result.isError = !0;
  125. if (response && 0 !== response.status) {
  126. if (429 === response.status || 503 === response.status) {
  127. result.errorMessage = "Translation Service unavailable";
  128. }
  129. else {
  130. result.errorMessage = `UnknownError occurred: [${null == response ? void 0 : response.status} ${null == response ? void 0 : response.statusText}]`;
  131. }
  132. }
  133. console.error("sendRequest()", result, response);
  134. }
  135. return result;
  136. }
  137.  
  138. var observer = new MutationObserver(function (mutations) {
  139. /** @type {Array<Element>} */
  140. var messageElms = [];
  141. mutations.forEach(mutation => {
  142. mutation.addedNodes.forEach((node) => {
  143. if (node instanceof HTMLElement) {
  144. messageElms = messageElms.concat(
  145. Array.from(node.querySelectorAll('[aria-roledescription="Message"]'))
  146. );
  147. }
  148. });
  149. });
  150.  
  151. // console.log(`node: Type: ${typeof node}. Value: ${node}`);
  152. messageElms.forEach(async (messageElm) => {
  153. const msgContentElm = messageElm.querySelector('[class^="contents_"] [id^="message-content-"]');
  154. if (!msgContentElm.querySelector(".myTranslateMsg")) {
  155. const translation = await translateText(msgContentElm.innerText);
  156.  
  157. // Create translation element template
  158. var msgTranslationText = document.createTextNode(translation.isError ? translation.errorMessage : translation.resultText);
  159. const msgTranslationElm = translateMsgElementTemplate.cloneNode();
  160. msgTranslationElm.appendChild(msgTranslationText);
  161.  
  162. // Add Translation to end of message
  163. msgContentElm.appendChild(horizontalLine.cloneNode());
  164. msgContentElm.appendChild(msgTranslationElm);
  165. }
  166. });
  167. });
  168.  
  169. // Start observing the target node for configured mutations
  170. observer.observe(document, { attributes: true, childList: true, subtree: true });
  171. })();