GitHub汉化插件

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

当前为 2021-08-28 提交的版本,查看 最新版本

  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.16
  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.bootcss.com/timeago.js/4.0.2/timeago.full.min.js
  19. // @require https://cdn.bootcss.com/jquery/3.4.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(".repository-content .f4"); //仓库简介翻译
  31. translateDesc(".gist-content [itemprop='about']"); // Gist 简介翻译
  32. traverseElement(document.body);
  33. watchUpdate();
  34.  
  35. function getLocales(lang) {
  36. if(lang.startsWith("zh")) { // zh zh-TW --> zh-CN
  37. lang = "zh-CN";
  38. }
  39. if(SUPPORT_LANG.includes(lang)) {
  40. return JSON.parse(GM_getResourceText(lang));
  41. }
  42. return {
  43. css: [],
  44. dict: {}
  45. };
  46. }
  47.  
  48. function translateRelativeTimeEl(el) {
  49. const datetime = $(el).attr('datetime');
  50. $(el).text(timeago.format(datetime, lang.replace('-', '_')));
  51. }
  52.  
  53. function translateElement(el) {
  54. // Get the text field name
  55. let k;
  56. if(el.tagName === "INPUT") {
  57. if (el.type === 'button' || el.type === 'submit') {
  58. k = 'value';
  59. } else {
  60. k = 'placeholder';
  61. }
  62. } else {
  63. k = 'data';
  64. }
  65.  
  66. const txtSrc = el[k].trim();
  67. const key = txtSrc.toLowerCase()
  68. .replace(/\xa0/g, ' ') // replace ' '
  69. .replace(/\s{2,}/g, ' ');
  70.  
  71. if(locales.dict[key]) {
  72. el[k] = el[k].replace(txtSrc, locales.dict[key])
  73. }
  74. }
  75.  
  76. function shoudTranslateEl(el) {
  77. const blockIds = ["readme", "wiki-content"];
  78. const blockClass = [
  79. "CodeMirror",
  80. "css-truncate", // 过滤文件目录
  81. "blob-code",
  82. "topic-tag", // 过滤标签,
  83. "text-normal", // 过滤repo name, 复现:https://github.com/search?q=explore
  84. ];
  85. const blockTags = ["CODE", "SCRIPT", "LINK", "IMG", "svg", "TABLE", "ARTICLE", "PRE"];
  86. const blockItemprops = ["name"];
  87.  
  88. if(blockTags.includes(el.tagName)) {
  89. return false;
  90. }
  91.  
  92. if(el.id && blockIds.includes(el.id)) {
  93. return false;
  94. }
  95.  
  96. if(el.classList) {
  97. for(let clazz of blockClass) {
  98. if(el.classList.contains(clazz)) {
  99. return false;
  100. }
  101. }
  102. }
  103.  
  104. if(el.getAttribute) {
  105. let itemprops = el.getAttribute("itemprop");
  106. if(itemprops) {
  107. itemprops = itemprops.split(" ");
  108. for(let itemprop of itemprops) {
  109. console.log(itemprop)
  110. if(blockItemprops.includes(itemprop)) {
  111. return false;
  112. }
  113. }
  114. }
  115. }
  116.  
  117. return true;
  118. }
  119.  
  120. function traverseElement(el) {
  121. if(!shoudTranslateEl(el)) {
  122. return
  123. }
  124.  
  125. for(const child of el.childNodes) {
  126. if(["RELATIVE-TIME", "TIME-AGO"].includes(el.tagName)) {
  127. translateRelativeTimeEl(el);
  128. return;
  129. }
  130.  
  131. if(child.nodeType === Node.TEXT_NODE) {
  132. translateElement(child);
  133. }
  134. else if(child.nodeType === Node.ELEMENT_NODE) {
  135. if(child.tagName === "INPUT") {
  136. translateElement(child);
  137. } else {
  138. traverseElement(child);
  139. }
  140. } else {
  141. // pass
  142. }
  143. }
  144. }
  145.  
  146. function watchUpdate() {
  147. const m = window.MutationObserver || window.WebKitMutationObserver;
  148. const observer = new m(function (mutations, observer) {
  149. for(let mutationRecord of mutations) {
  150. for(let node of mutationRecord.addedNodes) {
  151. traverseElement(node);
  152. }
  153. }
  154. });
  155.  
  156. observer.observe(document.body, {
  157. subtree: true,
  158. characterData: true,
  159. childList: true,
  160. });
  161. }
  162.  
  163. // translate "about"
  164. function translateDesc(el) {
  165. $(el).append("<br/>");
  166. $(el).append("<a id='translate-me' href='#' style='color:rgb(27, 149, 224);font-size: small'>翻译</a>");
  167. $("#translate-me").click(function() {
  168. // get description text
  169. const desc = $(el)
  170. .clone()
  171. .children()
  172. .remove()
  173. .end()
  174. .text()
  175. .trim();
  176.  
  177. if(!desc) {
  178. return;
  179. }
  180.  
  181. GM_xmlhttpRequest({
  182. method: "GET",
  183. url: `https://www.githubs.cn/translate?q=`+ encodeURIComponent(desc),
  184. onload: function(res) {
  185. if (res.status === 200) {
  186. $("#translate-me").hide();
  187. // render result
  188. const text = res.responseText;
  189. $(el).append("<span style='font-size: small'>由 <a target='_blank' style='color:rgb(27, 149, 224);' href='https://www.githubs.cn'>GitHub中文社区</a> 翻译👇</span>");
  190. $(el).append("<br/>");
  191. $(el).append(text);
  192. } else {
  193. alert("翻译失败");
  194. }
  195. }
  196. });
  197. });
  198. }
  199.  
  200. function translateByCssSelector() {
  201. if(locales.css) {
  202. for(var css of locales.css) {
  203. if($(css.selector).length > 0) {
  204. if(css.key === '!html') {
  205. $(css.selector).html(css.replacement);
  206. } else {
  207. $(css.selector).attr(css.key, css.replacement);
  208. }
  209. }
  210. }
  211. }
  212. }
  213. })();

QingJ © 2025

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