GitHub汉化插件

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

当前为 2021-09-26 提交的版本,查看 最新版本

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

QingJ © 2025

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