// ==UserScript== // @name VNDB优先原文和中文化 // @namespace http://tampermonkey.net/ // @version 2.0.0 // @description 优先显示原文(title->value),以及中文化(map[value]->value) // @author aotmd // @match https://vndb.org/* // @noframes // @license MIT // @run-at document-body // @grant none // @downloadURL none // ==/UserScript== /* // @require file://D:/IntelliJ IDEA 2019.3.3/IdeaProjects/自动化小程序/src/其他JS/VNDB优先原文和中文化.js */ /*通用主map,作用在全局*/ let map = { /*https://vndb.org/用户ID/ulist?vnlist=1*/ /*显示选项*/ "display options": "显示选项", "Order by": "排序方式", "Results": "显示数量", "Update": "更新", "Visible": "显示的", "columns": "列", /*排序标记*/ "Title": "标题", "Vote date": "评分时间", "Vote": "我的评分", "Rating": "评分", "Labels": "标识", "Added": "添加时间", "Modified": "修改时间", "Start date": "开始日期", "Finish date": "完成日期", "Release date": "发布日期", /*带排序标记*/ "Title ▴": "标题 ▴", "Vote date ▴": "评分时间 ▴", "Vote ▴": "我的评分 ▴", "Rating ▴": "评分 ▴", "Labels ▴": "标识 ▴", "Added ▴": "添加时间 ▴", "Modified ▴": "修改时间 ▴", "Start date ▴": "开始日期 ▴", "Finish date ▴": "完成日期 ▴", "Release date ▴": "发布日期 ▴", /*Opt*/ 'Notes': '笔记', 'Remove VN': '删除 VN', '-- add release --':'--添加版本--', /*状态*/ "Playing": "在玩", "Finished": "玩过", "Stalled": "搁置", "Dropped": "抛弃", "Wishlist": "愿望单", "Blacklist": "黑名单", /*VNDB封面插件翻译*/ "Always Show the VN Info": "始终显示 VN 信息", "Show NSFW Covers": "显示 NSFW 封面", "Disable tooltip": "禁用工具提示", "Skip Additional Info": "跳过附加信息", "Async Cover": "异步封面", "Query Mode": "查询方式", "Legacy View": "旧版视图", /*左侧栏*/ /*菜单*/ "Support VNDB": "赞助 VNDB", "Patreon": "Patreon", "SubscribeStar": "SubscribeStar", "Menu": "菜单", "Home": "首页", "Visual novels": "视觉小说", "Tags": "标签", "Releases": "版本", "Producers": "制作人", "Staff": "工作人员", "Characters": "人物", "Traits": "特征", "Users": "用户", "Recent changes": "最近更改", "Discussion board": "讨论区", "FAQ":"常见问题", "Random visual novel": "随机视觉小说", "Dumps": "转储", "API":"API", "Query": "查询", "Search": "搜索", "search": "搜索", /*我的*/ "My Profile": "我的个人资料", "My Visual Novel List": "我的视觉小说列表", "My Votes": "我的评分", "My Wishlist": "我的愿望单", "My Notifications": "我的通知", "My Recent Changes": "我的最近更改", "My Tags": "我的标签", "Image Flagging": "图片标记", "Add Visual Novel": "添加视觉小说", "Add Producer": "添加制作人", "Add Staff": "添加工作人员", "Logout": "退出登录", /*数据库统计*/ "Database Statistics": "数据库统计", "Visual Novels": "视觉小说" }; /*额外map,作用在指定页面*/ let otherMap = [ { /*作用页说明*/ name: '作用页说明', /*正则表达式*/ regular: /.+/i, /*map k->v*/ content: { } }, {} ]; /** ---------------------------map处理---------------------------*/ let pathname=window.location.pathname; otherMap.forEach((item)=>{ //当regular是正则才执行 if (item.regular!==undefined&&item.regular instanceof RegExp){ if (item.regular.test(pathname)){ //添加到主map,若存在重复项则覆盖主mpa Object.assign(map,item.content); console.log(item.name+',规则匹配:'+pathname+'->'+item.regular); } } }); /** ----------------------------END----------------------------*/ /** * 递归节点 * @param el 要处理的节点 * @param func 调用的函数 */ function 递归(el, func) { const nodeList = el.childNodes; for (let i = 0; i < nodeList.length; i++) { const node = nodeList[i]; if (node.nodeType === 1) { //为元素则递归 递归(node, func); let attribute, value, flag = false; if (node.nodeName === 'INPUT') { value = node.getAttribute('value'); attribute = 'value'; if(value==null||value.trim().length===0){ value = node.getAttribute('placeholder'); attribute = 'placeholder'; } flag = true; } else if (node.nodeName === 'TEXTAREA') { value = node.getAttribute('placeholder'); attribute = 'placeholder'; flag = true; } if (!flag) continue; func(node, value, attribute); } else if (node.nodeType === 3) { //为文本节点则处理数据 func(node, node.nodeValue); } } } (function () { /*title->value*/ (function(){ let doc = ''; let 线程锁 = false; /*立即执行*/ runMap(); window.setInterval(runMap, 100); function runMap() { if (doc === document.body.innerHTML) { // console.log('元素无变化'); return; } if (线程锁) { console.log('线程锁'); return; } 线程锁 = true; try { 递归(document.body, 数据处理); doc = document.body.innerHTML; } catch (e) {console.error('执行错误')} 线程锁 = false; } function 数据处理(node, value, attribute = '3') { if (value == null || value.trim().length === 0) return; value = value.trim(); if (attribute==='3'){ let title = node.parentNode.getAttribute("title"); //显示的部分不为中文或日文,并且交换的部分为中文或日文 if (title != null &&!/[\u4E00-\u9FA5ぁ-んァ-ヶ]+/.test(value) && /[\u4E00-\u9FA5ぁ-んァ-ヶ]+/.test(title)) { node.parentNode.setAttribute("title", value); node.nodeValue = title; console.log(value+'->'+title) } }else { let title = node.getAttribute("title"); //显示的部分不为中文或日文,并且交换的部分为中文或日文 if (title != null &&!/[\u4E00-\u9FA5ぁ-んァ-ヶ]+/.test(value) && /[\u4E00-\u9FA5ぁ-んァ-ヶ]+/.test(title)) { //若为通常节点则正常设置属性 node.setAttribute('title', value); node.setAttribute(attribute, title); console.log(value+'->'+title) } } } })(); /*map->value*/ (function(){ let doc = ''; let 线程锁 = false; /*立即执行*/ runMap(); window.setInterval(runMap, 100); function runMap() { if (doc === document.body.innerHTML) { // console.log('元素无变化'); return; } if (线程锁) {console.log('线程锁');return;} 线程锁 = true; try { 递归(document.body, 数据处理); doc = document.body.innerHTML; } catch (e) {console.error('执行错误')} 线程锁 = false; } function 数据处理(node, value, attribute = '3') { if (value == null || value.trim().length === 0) return; value = value.trim(); if (map[value] !== undefined) { if (attribute === '3') { //若为文本节点则追加父节点title属性 let title = node.parentNode.getAttribute('title'); if (title != null&&title.trim()!==value) { node.parentNode.setAttribute('title', title + ' ' + value); } else { node.parentNode.setAttribute('title', value); } node.nodeValue = map[value] } else { //若为通常节点则正常设置属性 node.setAttribute('title', value); node.setAttribute(attribute, map[value]) } } } })(); })(); /** 开启后通过控制台调用函数即可*/ let 翻译模式 = false; if (翻译模式) { /** * 导出新的已被翻译的内容到控制台显示 *
即手动在网页上改文本,注意: *
先在要翻译的文本中间写入翻译后的内容 *
然后用del和backspace删除前后内容 *
开启编辑模式: *
document.body.contentEditable='true'; *
document.designMode='on'; */ exportMap = function () { let addMap = {}; 递归(document.body, 数据处理); /*导出到控制台处理*/ console.log(JSON.stringify(addMap)); function 数据处理(node, value, attribute = '3') { if (value == null || value.trim().length === 0) return; value = value.trim(); //没有在map中找到翻译 if (map[value] === undefined) { //长度介于3~40,是中文、不是日文 if (/[\u4E00-\u9FA5]+/.test(value) && !/[ぁ-んァ-ヶ]+/.test(value)) { if (attribute === '3') { node = node.parentNode; } let title = node.getAttribute('title'); //如果title没有翻译,则记录 if (title !== null && map[title] === undefined) { addMap[title] = value; } } } } }; /*** 记录所有满足条件的未翻译内容
因为找不到上下文,所以一般不用*/ noMap = {}; /*** 用以复制value到title
若出现新元素,请手动通过控制台重新调用 */ copyToTitle = () => { //清空 noMap = {}; 递归(document.body, 数据处理); function 数据处理(node, value, attribute = '3') { if (value == null || value.trim().length === 0) return; value = value.trim(); //没有在map中找到翻译 if (map[value] === undefined) { //长度介于3~40,不为中文、日文 if (3 < value.length && value.length < 40 && !/[\u4E00-\u9FA5ぁ-んァ-ヶ]+/.test(value)) { //归一化处理 if (attribute === '3') {node = node.parentNode;} let title = node.getAttribute('title'); //title属性为中文或日文时不执行后续操作 if (title != null && /[\u4E00-\u9FA5ぁ-んァ-ヶ]+/.test(title)) { return; } //复制value->title node.setAttribute('title', value); //设置没有翻译的map标记 noMap[value] = value; } } } }; /*立即执行*/ copyToTitle(); }