现已支持标签TAG、作品详情页对标题和说明,以及评论区翻译!
// ==UserScript== // @name Pixiv 辅助翻译 // @namespace https://greasyfork.org/users/159546 // @version 1.1.7 // @description 现已支持标签TAG、作品详情页对标题和说明,以及评论区翻译! // @author LEORChn // @include *://www.pixiv.net/* // @run-at document-start // @grant GM_xmlhttpRequest // @connect translate.google.cn // ==/UserScript== var tag_trans=[ // 图型类 '漫画','漫画', 'うごイラ','动图', '厚塗り','多层上色', 'オリジナル','原创', // 角色类型类 'UTAU獣人','兽人虚拟歌手', 'ケモノ','野兽', 'wolf','狼', 'オオカミ','狼', 'ハスキー','哈士奇', 'kemono','毛怪', 'furry','兽人', '獣人','兽人', // 这个是日文的 '獸人','兽人', // 这个是繁体的 'オスケモ','雄兽', 'デブケモ','胖兽', 'ケモホモ','兽人同性向', 'ケモ交尾','兽性', // 作品名称类 '東#放課後サモナーズ','东#放课后召唤师', // 我在2018年9月初玩过,几号来着忘了 '放サモ','东放', 'housamo','东放', 'あらしのよるに','翡翠森林狼与羊(暴风雨之夜)', // 我在2019年3月初左右观看完了这个动画 'ひみつのともだち','秘密的朋友', 'ゼロから始める魔法の書','从零开始的魔法书', // 我在2019年5月8日14时观看完了这个动画,17时35分开始观看第12话,17时58分观看完毕撒花 // 人物名称类 '狼音アロ','狼音阿罗', 'モリタカ','犬塚戍孝', '犬塚モリタカ','犬塚戍孝', 'ホロケウカムイ','狼神神#', //ID为 20880639 的作者似乎打错了这个标签所以替换不到 'テムジン','铁木真', 'ガルム','加姆', 'ノーマッド(放サモ)','诺曼德(东放)', // 东#放课后里的虎兽人所用的化名 '主5','5号主人公(东放)', '獣の傭兵','佣兵(零书)', // 着装类 'ふんどし','兜裆布', '褌','兜裆布', 'ラバースーツ','橡胶紧身衣', '靴下','袜子', // 器官类 '腹筋','腹肌', 'ㄋㄟㄋㄟ','胸部', // 行为类 '金的','捣蛋', '猿轡','封口', '目隠し','蒙眼', '拘束','捆绑Play' ]; (function(){ recheck(); })(); function recheck(){ init(); if(load())return; setTimeout(recheck,100); } function init(){ // call once when start loading page if(ft('body').length==0) return; main_daemon(); //inited=true; // } function load(){ // call once when loaded page if(document.readyState.toLowerCase()=='complete'){ main_daemon();// write code here return true; } } var daemonLauncher; function main_daemon(){ if(daemonLauncher) return; daemonLauncher = setInterval(main_do, 3000); main_do(); } function main_do(){ tagTranslate_illust(); // 个人空间 > 作品列表页面 - 标签 tagTranslate_illust_single(); // 作品页面 - 标签 tagTranslate_bookmark(); // 个人空间 > 我的收藏页面 - 标签 tagTranslate_addBookmark(); // 作品页面 > 编辑心页面 - 添加收藏时的可选择标签 tagTranslate_member_tag_all(); // 个人空间 - 所有绘制的作品包含的标签(包含频率降序) detailTranslate_illust(); // 作品页面 - 自动翻译作品标题和简介 button_trans_comment(); // 作品页面 - 添加评论翻译的按钮 unblock_popular_illust_overlay(); // 搜索页面 - 有作品在“查看更多「xxx」的热门作品”面板的下方,点击该面板提示开通会员,用这个隐藏会员面板 popup_popular_order(); // 【预想】搜索页面 - 突破会员限制 delete_illust_expanded_ad(); // 作品页面 - 在展开多页作品后最底部会出现广告,删除它以节省滚动操作 } // ========== 功能性 function unblock_popular_illust_overlay(){ // 搜索页面 热门作品提示 var v = $$('.popular-introduction-overlay'); for(var i=0; i<v.length; i++) v[i].style.display = 'none'; } function delete_illust_expanded_ad(){ // 作品页面 广告 var v = $('main section div iframe'); if(v) v.remove(); } function popup_popular_order(){ // 【预想测试】【失败】突破会员限制实现某些功能 var v = $('.require-premium-popular_d'), v2 = $('._popular-search-select-popup-container'); if(v && v2){ v.onmousemove = v2.onmousemove = function(){ v2.style.setProperty('display', 'block', 'important'); v2.style.marginTop = '-5px'; } v.onmouseleave = v2.onmouseleave = function(){ v2.style.display = ''; } } } // ========== 作品页面标签(单图预览和评论区) 以下 function tagTranslate_illust_single(){ if(!location.pathname.startsWith('/artworks/')) return; var tags=$$('figcaption footer>ul>li'); if(tags.length==0)return; for(var li=0;li<tags.length;li++){ var c=tags[li].childNodes, tt=tagDict(c[0].innerText); tags[li].style.display='inline-block'; if(tt){ c[0].childNodes[0].innerText=tt; if(c.length>1) c[1].remove(); } } } // ----- 作品页标题和说明 以下 var ID_TRANSLATION_SOURCE='leorchn_icon_google_translate', ICON_TRANSLATION_SOURCE='https://translate.google.cn/favicon.ico', ID_TRANSLATED_TITLE = 'leorchn_translated_title', ID_TRANSLATED_DESC = 'leorchn_translated_desc', block_detail_root; function detailTranslate_illust(){ if(!location.pathname.startsWith('/artworks/')) return; // if(!location.href.includes('mode=medium')) return; var detail_block_post_time = $('figcaption div[title]'); if(!detail_block_post_time) return; block_detail_root = detail_block_post_time.parentElement; if(!fv(ID_TRANSLATION_SOURCE)){ var img=ct('img'); img.id=ID_TRANSLATION_SOURCE; img.src=ICON_TRANSLATION_SOURCE; img.style.cssText='width:24px; float:right'; block_detail_root.insertBefore(img, block_detail_root.children[0]); } trans_title(); trans_desc(); } function trans_title(){ // 作品标题 var p=$('figcaption div>h1'), d=fv(ID_TRANSLATED_TITLE); if(!p) return; if(d){ if(d.title == d.innerText){ // 标题块中缓存的原文与自身内容一致,表示翻译完成但与原文一致或无法翻译 p.style.backgroundColor='#c0e0ff'; // 译文等于原文自身,给原文块加蓝底表示无需翻译 d.style.display='none'; } if(d.title == p.innerText) return; // 标题块中缓存的与原文一致,表示页面没有变化,取消翻译 p.style.backgroundColor=''; // 页面有变化,如果修改过蓝底的就重置它 d.parentElement.remove(); d = undefined; } /* 要解释为什么不能在这两个if之间用else连起来,先看看进入下一个if的两种情况: 1.新建页面,没有找到翻译块 2.页面更改,并进入了上一个if。并且没有触碰到return,导致d被设为undefined 很明显,2号情况决定了这俩if不能用else连起来 */ if(!d){ // 因为标题块没有双层嵌套,二次读取会导致缓存文字块被误翻译 var q = ct('h1'); d = trans_create_block(''); q.className = p.className; d.id=ID_TRANSLATED_TITLE; d.title=p.innerText; // 标题块中缓存原文 q.appendChild(d); p.parentElement.insertBefore(q, p.nextElementSibling); } googleTranslateProxy(p.innerText, fv(ID_TRANSLATED_TITLE), trans_title); } function trans_desc(){ // 作品描述 var p=$('figcaption div>h1+div>div'), d=fv(ID_TRANSLATED_DESC); if(!p) return; if(d){ if(p.innerText.replace(/\s/g,'') == d.innerText.replace(/\s/g,'')){ // 标题块中缓存的原文与自身内容一致,表示翻译完成但与原文一致或无法翻译 p.style.backgroundColor='#c0e0ff'; // 译文等于原文自身,给原文块加蓝底表示无需翻译 d.style.display='none'; } if(d.title == p.innerText) return; // 标题块中缓存的与原文一致,表示页面没有变化,取消翻译 p.style.backgroundColor=''; // 页面有变化,如果修改过蓝底的就重置它 d.remove(); d = undefined; } // 这里还用复制一遍解释么,标题块那个方法里有过了 if(!d){ d = trans_create_block('') d.id=ID_TRANSLATED_DESC; d.title=p.innerText; // 标题块中缓存原文 p.parentElement.appendChild(d); } googleTranslateProxy(p.innerText.replace(/\n/g, CRLF), fv(ID_TRANSLATED_DESC), trans_desc); } // ----- 评论区翻译按钮 以下 var ID_COMMENT_TRANSLATE_TRIGGER = 'leorchn_comment_translate_trigger', ID_COMMENT_TRANSLATION_BLOCK = 'leorchn_comment_translation_block'; function button_trans_comment(){ var p=$$('main section li div>span+span+span'); for(var i=0;i<p.length;i++){ var parent = p[i].parentElement; if(parent.getElementsByClassName(ID_COMMENT_TRANSLATE_TRIGGER).length == 0){ var d=ct('span', '翻译'); d.className=p[i].className + ' ' + ID_COMMENT_TRANSLATE_TRIGGER; d.style.marginLeft='16px'; d.onclick=function(){ var _this=this, commentRoot=this.parentElement.previousElementSibling; if(commentRoot.getElementsByClassName(ID_COMMENT_TRANSLATION_BLOCK).length == 0){ var tb = trans_create_block('') tb.className = ID_COMMENT_TRANSLATION_BLOCK; commentRoot.appendChild(tb); } googleTranslateProxy(commentRoot.children[0].innerText, commentRoot.getElementsByClassName(ID_COMMENT_TRANSLATION_BLOCK)[0]); } parent.appendChild(d); } } } // ========== 作品页面(单图预览和评论区) 以上 // ========== 自己和其他画师空间 以下 // ----- 主页(显示全部作品、筛选其中一个标签) function tagTranslate_illust(){ if(location.pathname != '/member_illust.php')return; var tags=$$('div>nav+div>div>div>ul>li'); if(tags.length==0)return; for(var li=0;li<tags.length;li++){ var c=tags[li].children, tt=tagDict(c[0].innerText); if(tt){ c[0].innerText=tt; } } } // ----- 所有已加心的作品 页面 function tagTranslate_bookmark(){ if(location.pathname != '/bookmark.php')return; tagTranslate_mode_tagCloud(); } // ----- 所有绘制的作品包含的标签(包含频率降序) function tagTranslate_member_tag_all(){ if(location.pathname != '/member_tag_all.php')return; tagTranslate_mode_tagCloud(); var tags=fc('tag-list'); if(tags.length==0)return; tags=tags[0].getElementsByTagName('dd'); for(var dd=0;dd<tags.length;dd++){ var c=tags[dd].childNodes[0].childNodes; for(var li=0;li<c.length;li++){ var tt=tagDict(c[li].innerText); if(tt){ var atag= c[li].getElementsByTagName('a')[0]; atag.innerText=tt; atag.style.backgroundColor='#ffd040'; } } } } // ----- 加心/编辑心 页面 function tagTranslate_addBookmark(){ if(location.pathname != '/bookmark_add.php')return; var tags=fc('tag-cloud'); if(tags.length==0)return; for(var g=0,gt=tags;g<gt.length;g++){ tags=gt[g].childNodes; for(var li=0;li<tags.length;li++){ try{ // 作品自带标签的列表中,“原创”后面竟然跟一个空白的元素不知道为啥 var c=tags[li].childNodes[0].childNodes, cl=c.length-1, tt=tagDict(c[cl].nodeValue); if(tt) c[cl].nodeValue=tt; }catch(e){} } } } // ========== 自己和其他画师空间 以上 // ----- 模板:标签云 function tagTranslate_mode_tagCloud(){ var tags=fc('tagCloud'); if(tags.length==0)return; tags=tags[0].childNodes; for(var li=0;li<tags.length;li++){ try{ if(tags[li].className.includes('level0')) continue; }catch(e){ continue; } tags[li].style.display='inline-block'; var c=tags[li].childNodes[0].childNodes, tt=tagDict(c[0].nodeValue); if(tt) c[0].nodeValue=tt; } } // ----- 标签 字典翻译 function tagDict(origin){ for(var i=0;i<tag_trans.length;i+=2) if(origin==tag_trans[i]) return tag_trans[i+1]; } var CRLF = '<br>', CRLF_REGEX_ESCAPE = '<br>'; // ----- 创建和编辑外部翻译块 function trans_create_block(oriText, existsBlock){ var n=existsBlock? existsBlock: ct('p'); n.innerHTML=oriText? googleTranslate_get(oriText).replace(new RegExp(CRLF_REGEX_ESCAPE, 'g'), CRLF): '翻译中'; n.style.backgroundColor='#d0ffd0'; return n; } //----- my ezjs lib function fv(id){return document.getElementById(id);} function ft(tag){return document.getElementsByTagName(tag);} function fc(cname){return document.getElementsByClassName(cname);} function ct(tag, t){var d=document.createElement(tag); if(t)d.innerText=t; return d;} function $(s){return document.querySelector(s);} function $$(s){return document.querySelectorAll(s);} function msgbox(msg){alert(msg);} function inputbox(title,defalt){return prompt(title,defalt);} function pl(s){console.log(s);} function googleTranslateProxy(origText, postTo, invokeWhenFinish){ /* 如果此处判定成功则是原文过长,感觉没有翻译成功的希望,暂时隐藏它。 此处的 4500字节 = 500亚洲文字 或者 4500英文字母和数字 空格和某些需要转义的字符和标点每个占 3字节 亚洲文字及标点每个占 9字节。例如“我”这个字转义后是 %E6%88%91 即占用 9字节 */ if(encodeURI(origText).length > 4500){ postTo.style.display = 'none'; } googleTranslate(origText, function(r){ if(!r.responseText || r.responseText.length<20) if(googleTranslateProxy(origText, postTo) || true) return; trans_create_block(r.responseText, postTo); if(postTo.style.display == 'none') postTo.style.display = ''; // 已翻译完成,取消隐藏(如果之前觉得这个翻译块没有翻译成功的希望并被隐藏的话) if(invokeWhenFinish) try{ invokeWhenFinish(); }catch(e){} }); } function googleTranslate_get(origin){ return new RegExp('<div\\s.{0,15}class=\\"t0\\".{0,15}>(.*)<\\/div><f').exec(origin)[1]; } function googleTranslate(word,dofun,dofail){ http('get','https://translate.google.cn/m?hl=zh-CN&sl=auto&tl=zh-CN&ie=UTF-8&prev=_m&q='+encodeURI(word),'',dofun,dofail); } function http(_method,_url,formdata,dofun,dofail){ GM_xmlhttpRequest({ method: _method.toUpperCase(), url: _url, data: formdata, headers:{}, onload: dofun, onerror: dofail }); }