GitHub汉化插件

GitHub汉化插件,包含人机翻译

当前为 2021-04-19 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name GitHub Internationalization
  3. // @name:zh-CN GitHub汉化插件
  4. // @name:ja GitHub日本語
  5. // @namespace https://github.com/k1995/github-i18n-plugin/
  6. // @version 0.13
  7. // @description Translate GitHub.com
  8. // @description:zh GitHub汉化插件,包含人机翻译
  9. // @description:zh-CN GitHub汉化插件,包含人机翻译
  10. // @description:ja GitHub日本語プラグイン
  11. // @author k1995
  12. // @match https://github.com/*
  13. // @match https://gist.github.com/*
  14. // @grant GM_xmlhttpRequest
  15. // @grant GM_getResourceText
  16. // @resource zh-CN https://www.githubs.cn/raw-githubusercontent/k1995/github-i18n-plugin/master/locales/zh-CN.json?v=20210407
  17. // @resource ja https://www.githubs.cn/raw-githubusercontent/k1995/github-i18n-plugin/master/locales/ja.json
  18. // @require https://cdn.bootcdn.net/ajax/libs/timeago.js/4.0.2/timeago.full.min.js
  19. // @require https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js
  20. // ==/UserScript==
  21.  
  22. (function() {
  23. 'use strict';
  24.  
  25. const SUPPORT_LANG = ["zh-CN", "ja"];
  26. const lang = (navigator.language || navigator.userLanguage);
  27. const locales = getLocales(lang)
  28.  
  29. translateByCssSelector();
  30. translateDesc();
  31. traverseElement(document.body);
  32. watchUpdate();
  33.  
  34. function getLocales(lang) {
  35. if(lang.startsWith("zh")) { // zh zh-TW --> zh-CN
  36. lang = "zh-CN";
  37. }
  38. if(SUPPORT_LANG.includes(lang)) {
  39. return JSON.parse(GM_getResourceText(lang));
  40. }
  41. return {
  42. css: [],
  43. dict: {}
  44. };
  45. }
  46.  
  47. function translateRelativeTimeEl(el) {
  48. const datetime = $(el).attr('datetime');
  49. $(el).text(timeago.format(datetime, lang.replace('-', '_')));
  50. }
  51.  
  52. function translateElement(el) {
  53. // Get the text field name
  54. let k;
  55. if(el.tagName === "INPUT") {
  56. if (el.type === 'button' || el.type === 'submit') {
  57. k = 'value';
  58. } else {
  59. k = 'placeholder';
  60. }
  61. } else {
  62. k = 'data';
  63. }
  64.  
  65. const txtSrc = el[k].trim();
  66. const key = txtSrc.toLowerCase()
  67. .replace(/\xa0/g, ' ') // replace ' '
  68. .replace(/\s{2,}/g, ' ');
  69.  
  70. if(locales.dict[key]) {
  71. el[k] = el[k].replace(txtSrc, locales.dict[key])
  72. }
  73. }
  74.  
  75. function shoudTranslateEl(el) {
  76. const blockIds = ["readme", "wiki-content"];
  77. const blockClass = [
  78. "CodeMirror",
  79. "css-truncate" // 过滤文件目录
  80. ];
  81. const blockTags = ["CODE", "SCRIPT", "LINK", "IMG", "svg", "TABLE", "ARTICLE", "PRE"];
  82.  
  83. if(blockTags.includes(el.tagName)) {
  84. return false;
  85. }
  86.  
  87. if(el.id && blockIds.includes(el.id)) {
  88. return false;
  89. }
  90.  
  91. if(el.classList) {
  92. for(let clazz of blockClass) {
  93. if(el.classList.contains(clazz)) {
  94. return false;
  95. }
  96. }
  97. }
  98.  
  99. return true;
  100. }
  101.  
  102. function traverseElement(el) {
  103. if(!shoudTranslateEl(el)) {
  104. return
  105. }
  106.  
  107. for(const child of el.childNodes) {
  108. if(["RELATIVE-TIME", "TIME-AGO"].includes(el.tagName)) {
  109. translateRelativeTimeEl(el);
  110. return;
  111. }
  112.  
  113. if(child.nodeType === Node.TEXT_NODE) {
  114. translateElement(child);
  115. }
  116. else if(child.nodeType === Node.ELEMENT_NODE) {
  117. if(child.tagName === "INPUT") {
  118. translateElement(child);
  119. } else {
  120. traverseElement(child);
  121. }
  122. } else {
  123. // pass
  124. }
  125. }
  126. }
  127.  
  128. function watchUpdate() {
  129. const m = window.MutationObserver || window.WebKitMutationObserver;
  130. const observer = new m(function (mutations, observer) {
  131. for(let mutationRecord of mutations) {
  132. for(let node of mutationRecord.addedNodes) {
  133. traverseElement(node);
  134. }
  135. }
  136. });
  137.  
  138. observer.observe(document.body, {
  139. subtree: true,
  140. characterData: true,
  141. childList: true,
  142. });
  143. }
  144.  
  145. // translate "about"
  146. function translateDesc() {
  147. $(".repository-content .f4").append("<br/>");
  148. $(".repository-content .f4").append("<a id='translate-me' href='#' style='color:rgb(27, 149, 224);font-size: small'>翻译</a>");
  149. $("#translate-me").click(function() {
  150. // get description text
  151. const desc = $(".repository-content .f4")
  152. .clone()
  153. .children()
  154. .remove()
  155. .end()
  156. .text()
  157. .trim();
  158.  
  159. if(!desc) {
  160. return;
  161. }
  162.  
  163. GM_xmlhttpRequest({
  164. method: "GET",
  165. url: `https://www.githubs.cn/translate?q=`+ encodeURIComponent(desc),
  166. onload: function(res) {
  167. if (res.status === 200) {
  168. $("#translate-me").hide();
  169. // render result
  170. const text = res.responseText;
  171. $(".repository-content .f4").append("<span style='font-size: small'>由 <a target='_blank' style='color:rgb(27, 149, 224);' href='https://www.githubs.cn'>GitHub中文社区</a> 翻译👇</span>");
  172. $(".repository-content .f4").append("<br/>");
  173. $(".repository-content .f4").append(text);
  174. } else {
  175. alert("翻译失败");
  176. }
  177. }
  178. });
  179. });
  180. }
  181.  
  182. function translateByCssSelector() {
  183. if(locales.css) {
  184. for(var css of locales.css) {
  185. if($(css.selector).length > 0) {
  186. if(css.key === '!html') {
  187. $(css.selector).html(css.replacement);
  188. } else {
  189. $(css.selector).attr(css.key, css.replacement);
  190. }
  191. }
  192. }
  193. }
  194. }
  195. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址