Pixiv Tag Translation/Replacement

Shows translations of tags on Pixiv and prompts for untranslated tags.

目前為 2016-09-11 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name Pixiv Tag Translation/Replacement
  3. // @description Shows translations of tags on Pixiv and prompts for untranslated tags.
  4. // @namespace http://scripts.chris.charabaruk.com/pixiv.net/~tag-translation
  5. // @author coldacid
  6. // @include http://www.pixiv.net/
  7. // @include http://www.pixiv.net/*
  8. // @include http://pixiv.net/
  9. // @include http://pixiv.net/*
  10. // @include https://www.pixiv.net/
  11. // @include https://www.pixiv.net/*
  12. // @include https://pixiv.net/
  13. // @include https://pixiv.net/*
  14. // @version 1.1
  15. // @grant none
  16. // ==/UserScript==
  17.  
  18. var TagsCollection;
  19. {
  20. const USER_DATA_KEY = 'com.charabaruk.chris.pixiv.net.tag-translation';
  21. let version = 1;
  22. let map = null;
  23.  
  24. let loadMap = function () {
  25. var userData = window.localStorage[USER_DATA_KEY];
  26. if (userData) {
  27. userData = JSON.parse(userData);
  28. } else {
  29. userData = {version: 1};
  30. }
  31.  
  32. version = userData.version || 1;
  33.  
  34. var tags = userData.tags || {
  35. "R-18": null,
  36. "3D": null
  37. };
  38. tags[Symbol.iterator] = function* () { for (var tag in this) yield [tag, this[tag]]; }
  39. map = new Map(tags);
  40. };
  41. let saveMap = function () {
  42. // so we don't overwrite changes made in other tabs, grab the current data first when saving
  43. var userData = JSON.parse(window.localStorage[USER_DATA_KEY] || `{version: ${version}, tags: {}}`);
  44. for (var [k, v] of map.entries()) { userData.tags[k] = v; } // yes, overwrite existing tags when merging
  45.  
  46. window.localStorage[USER_DATA_KEY] = JSON.stringify(userData);
  47. };
  48. window.addEventListener('storage', evt => {
  49. if (evt.key !== USER_DATA_KEY) { return; }
  50. console.info("Another tab has updated tag translations, merging");
  51. var tags = JSON.parse(evt.newValue || "{tags: null}").tags;
  52. if (!tags) { return; }
  53. for(var key of Object.getOwnPropertyNames(tags)) {
  54. map.set(key, tags[key]); // take remove version over existing one
  55. }
  56. }, false);
  57.  
  58. TagsCollection = function TagsCollection () {
  59. loadMap();
  60. };
  61.  
  62. Object.defineProperty(TagsCollection.prototype, 'version', {value: version});
  63.  
  64. TagsCollection.prototype.has = function (tag) { return map.has(tag); };
  65. TagsCollection.prototype.get = function (tag) { return map.get(tag) || tag; };
  66. TagsCollection.prototype.set = function (tag, translation) {
  67. if (translation === undefined) {
  68. if (tag.entries) {
  69. for (var [key, value] of tag.entries()) {
  70. map.set(key, value);
  71. }
  72. } else if (tag[Symbol.iterator]) {
  73. for (var [key, value] of tag) {
  74. map.set(key, value);
  75. }
  76. } else if (tag instanceof Object) {
  77. for (var key of Object.getOwnPropertyNames(tag)) {
  78. map.set(key, tag[key]);
  79. }
  80. } else {
  81. throw new Error('missing translation');
  82. }
  83. } else {
  84. map.set(tag, translation);
  85. }
  86.  
  87. saveMap();
  88. };
  89.  
  90. TagsCollection.prototype.tags = function* () { for (var entry in map.entries()) yield entry; }
  91. TagsCollection.prototype.translations = function () {
  92. var reversed = {};
  93. reversed[Symbol.iterator] = function* () { for (var key in this) yield [key, this[key]]; }
  94.  
  95. for (var [key, value] of map.entries()) {
  96. reversed[value] = reversed[value] || [];
  97. reversed[value].push(key);
  98. }
  99.  
  100. return reversed;
  101. };
  102.  
  103. TagsCollection.prototype.translatedAs = function (translation) {
  104. translation = translation || '';
  105.  
  106. var tags = [];
  107. for (var [key, value] of map.entries()) {
  108. if ((value || '').toLowerCase() === translation.toLowerCase())
  109. tags.push(key);
  110. }
  111. return tags;
  112. };
  113. }
  114.  
  115. function setTagText($element, tag) {
  116. var originalTag = $element.text(),
  117. newText = `${tag} (${originalTag})`;
  118. if ($element[0].nodeType == Node.TEXT_NODE) {
  119. $element[0].textContent = newText;
  120. } else {
  121. $element.text(newText);
  122. }
  123. }
  124.  
  125. function GM_main ($) {
  126. var tags = new TagsCollection();
  127. window.translatedTags = tags;
  128.  
  129. var tagSelectors = [
  130. 'li.tag > a:not([class~="portal"])',
  131. 'div.tag-name',
  132. 'section.favorite-tag > ul.favorite-tags > li > a',
  133. 'nav.breadcrumb > span a[href^="/tags.php?tag="] > span[itemprop="title"]',
  134. 'ul.tagCloud > li > a',
  135. 'ul.tags > li > a:not([class~="tag-icon"])',
  136. 'table.ws_table td.td2 > a[href^="personal_tags.php?tag="]'
  137. ].join(', ');
  138.  
  139. var untranslated = new Map();
  140. // content page regular tags, home page featured tags, home page favorite tags
  141. $(tagSelectors)
  142. .contents()
  143. .filter((i, n) => n.nodeType == Node.TEXT_NODE) // only get the text nodes within the selected elements
  144. .each((idx, el) => {
  145. var $el = $(el),
  146. tag = $el.text();
  147.  
  148. if (/^[\x20-\x7e]*$/.test(tag)) {
  149. console.log(`"${tag}" only uses ASCII printable characters, skipping`);
  150. return;
  151. }
  152.  
  153. if (tags.has(tag)) {
  154. setTagText($el, tags.get(tag));
  155. } else {
  156. let elList = untranslated.has(tag) ? untranslated.get(tag) : [];
  157. elList.push($el);
  158. untranslated.set(tag, elList);
  159. }
  160. });
  161.  
  162. if (untranslated.size > 0) {
  163. var taglist = Array.from(untranslated.keys()).join(', '),
  164. tagcount = untranslated.size;
  165. if (window.confirm(`There are ${tagcount} untranslated tags. Want to translate?\n\nTags: ${taglist}`)) {
  166. var translations = new Map(), i = 1;
  167. for (var [tag, $els] of untranslated.entries()) {
  168. // try getting a translated version anyway, just in case it got translated on another tab
  169. var translated = window.prompt(
  170. `Translation for: ${tag}\n\nLeave empty to cancel translating, leave as-is to skip [${i++}/${tagcount}]`,
  171. tags.get(tag));
  172. if (!translated) { break; }
  173.  
  174. // only save if the translation is different from the original tag
  175. if (tag !== translated) {
  176. translations.set(tag, translated);
  177. $els.forEach($el => setTagText($el, translated));
  178. }
  179. }
  180. tags.set(translations);
  181. }
  182. }
  183. }
  184.  
  185. if (typeof jQuery === 'function') {
  186. console.log(`Using local jQuery, version ${jQuery.fn.jquery}`);
  187. GM_main(jQuery);
  188. } else {
  189. console.log('Loading jQuery from Google CDN');
  190. add_jQuery(GM_main, '1.11.1');
  191. }
  192.  
  193. function add_jQuery(callbackFn, jqVersion) {
  194. jqVersion = jqVersion || "1.11.1";
  195. var D = document,
  196. targ = D.getElementsByTagName('head')[0] || D.body || D.documentElement,
  197. scriptNode = D.createElement('script');
  198.  
  199. scriptNode.src = `//ajax.googleapis.com/ajax/libs/jquery/${jqVersion}/jquery.min.js`;
  200. scriptNode.addEventListener('load', function () {
  201. var scriptNode = D.createElement('script');
  202. scriptNode.textContent =
  203. 'var gm_jQuery = jquery.noConflict(true);\n'
  204. + '(' + callbackFn.toString() + ')(gm_jQuery);';
  205. targ.appendChild(scriptNode);
  206. }, false);
  207. targ.appendChild(scriptNode);
  208. }

QingJ © 2025

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