GitHub汉化油猴脚本

GitHub汉化插件,含个性翻译

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

QingJ © 2025

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