Greasy Fork is available in English.

GitHub local

Translate GitHub.com

נכון ליום 24-04-2021. ראה הגרסה האחרונה.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

  1. // ==UserScript==
  2. // @name GitHub local
  3. // @name:zh-CN GitHub汉化油猴脚本
  4. // @namespace https://github.com/Iuleoo/GitHub_loc
  5. // @version 2.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=20210424
  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. })();