Greasy Fork is available in English.

UserScript Translation Engine

Translate strings by given translations

Fra 20.12.2014. Se den seneste versjonen.

Dette scriptet burde ikke installeres direkte. Det er et bibliotek for andre script å inkludere med det nye metadirektivet // @require https://update.greatest.deepsurf.us/scripts/7081/28938/UserScript%20Translation%20Engine.js

  1. // ==UserScript==
  2. // @name UserScript Translation Engine
  3. // @namespace org.jixun.us.translation
  4. // @description Translate strings by given translations
  5. // @version 1.0
  6. // @run-at document-start
  7. // @grant none
  8. // ==/UserScript==
  9.  
  10. var Translation = (function () {
  11. var _each = function (arr, eachCb, defValue) {
  12. if (!arr || !arr.length) return ;
  13.  
  14. for (var i = arr.length, ret; i-- ; )
  15. // If there's something to return, then return it.
  16. if (ret = eachCb (arr[i], i))
  17. return ret;
  18.  
  19. return defValue ;
  20. };
  21. var _filterPop = function (arr, fn) {
  22. if (arr && arr.length)
  23. for (var i = arr.length; i--; )
  24. if (fn(arr[i]))
  25. return arr[i];
  26. };
  27.  
  28. var extend = function (src) {
  29. var args = arguments, argl = args.length;
  30. for (var i = 1; i < argl; i++) {
  31. for (var x in args[i]) {
  32. if (args[i].hasOwnProperty(x)) {
  33. if (src[x] instanceof Object) {
  34. extend (src[x], args[i][x]);
  35. } else {
  36. src[x] = args[i][x];
  37. }
  38. }
  39. }
  40. }
  41.  
  42. return src;
  43. };
  44.  
  45. var Translation = function (lang) {
  46. this.resetLang ();
  47. this.setLang (lang);
  48. };
  49.  
  50. // 获取浏览器预设语言
  51. Translation.getLang = function (lang) {
  52. var x = _filterPop(navigator.languages.slice().reverse(), function (x) { return lang[x] });
  53. return lang[x] || {};
  54. };
  55.  
  56. Translation.prototype = {
  57. run: function (node) {
  58. var self = this;
  59. node = node || document.body || document;
  60.  
  61. this.mo = new MutationObserver (function (m) {
  62. _each (m, function (q) {
  63. _each (q.addedNodes, function (e) {
  64. // Firebug keep injects their stuff, ignore
  65. if (e.className && e.className.indexOf ('firebug') != -1)
  66. return ;
  67.  
  68. self.translateNode (e);
  69. });
  70.  
  71. if (q.type == 'attributes') {
  72. var x = self.findAttrTranslation (q.target, q.attributeName, q.target.getAttribute(q.attributeName));
  73. if (x) {
  74. q.target.setAttribute (q.attributeName, x);
  75. }
  76. }
  77. });
  78. });
  79.  
  80. this.mo.observe (node, {
  81. childList: true,
  82. subtree: true,
  83. characterData: true,
  84. attributes: true
  85. });
  86.  
  87. this.translateNode (node);
  88. },
  89.  
  90. excludeTags: ['code', 'pre', 'script', 'style', 'link', 'meta'],
  91. excludeFromTag: function (node, tagName) {
  92. var n = node;
  93. while (n = n.parentNode)
  94. if (-1 != this.excludeTags.indexOf(n))
  95. return true;
  96.  
  97. return false;
  98.  
  99. },
  100.  
  101. translateNode: function (node) {
  102. _each(node.getElementsByTagName('*'), this.applyAttr.bind(this));
  103.  
  104. var self = this;
  105.  
  106. var walker = document.createTreeWalker (node, 4 /* NodeFilter.SHOW_TEXT */, function (textNode) {
  107. return (
  108. self.excludeFromTag(textNode.parentNode)
  109. || textNode.nodeValue.trim() === ''
  110. ? 2 /* NodeFilter.FILTER_REJECT */
  111. : 1 /* NodeFilter.FILTER_ACCEPT */
  112. );
  113. }, false);
  114.  
  115. // Loop through text nodes.
  116. while (node = walker.nextNode())
  117. this.applyNode (node);
  118. },
  119.  
  120. applyNode: function (node) {
  121. this.applyText (node);
  122. this.applyAttr (node.parentNode);
  123. },
  124.  
  125. findTranslation: function (lang, str) {
  126. var args = arguments;
  127. var stack = [ lang ];
  128. var r = lang;
  129. for (var i = 2; i < args.length; i++) {
  130. r = r[args[i]];
  131. if (!r) break;
  132.  
  133. stack.push (r);
  134. }
  135.  
  136. if (stack.length) {
  137. for (i = stack.length; i--; ) {
  138. if (stack[i][str] && 'string' == typeof stack[i][str]) {
  139. return stack[i][str];
  140. }
  141.  
  142. if (stack[i].regex) {
  143. r = _filterPop(stack[i].regex, function (re) {
  144. return re[0].test( str );
  145. });
  146. if (r) return str.replace(r[0], r[1]);
  147. }
  148. }
  149. }
  150. },
  151.  
  152. applyText: function (node) {
  153. var self = this;
  154. var v = node.nodeValue.trim();
  155. if (!v) return ;
  156.  
  157. var l = (this.findTranslation (this.lang.node, v, 'tag', node.parentNode.tagName)
  158. || this.findTranslation (this.lang.node, v, 'str'));
  159.  
  160. if (l) node.nodeValue = l;
  161. },
  162.  
  163. findAttrTranslation: function (node, attrName, attrVal) {
  164. return (this.findTranslation (this.lang.attr, attrVal, 'tag', node.tagName, attrName)
  165. || this.findTranslation (this.lang.attr, attrVal, 'str', attrName))
  166. },
  167.  
  168. applyAttr: function (node) {
  169. var self = this;
  170. var tag = node.tagName;
  171. var l;
  172.  
  173. _each(node.attributes, function (attr) {
  174. l = self.findAttrTranslation (node, attr.name, attr.value);
  175.  
  176. if (l) attr.value = l;
  177. });
  178. },
  179.  
  180. /**
  181. * Set translation profile
  182. * @param {Object} lang Language translation hashmap.
  183. */
  184. setLang: function (lang) {
  185. if (lang instanceof Object)
  186. extend (this.lang, lang);
  187. },
  188.  
  189. /**
  190. * Get translation
  191. * @return {Object} Language Translation hashmap
  192. */
  193. getLang: function () {
  194. return extend({}, this.lang);
  195. },
  196.  
  197. resetLang: function () {
  198. this.lang = {node: {tag: {}, str: {}}, attr: {tag: {}, str: {}}};
  199. }
  200. };
  201.  
  202. return Translation;
  203. })();