Greasy Fork

Greasy Fork is available in English.

VNDB优先原文和中文化

优先显示原文(title->value),以及中文化(map[value]->value)

目前为 2022-04-15 提交的版本,查看 最新版本

// ==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
// ==/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 (翻译模式) {
    /**
     * 导出新的已被翻译的内容到控制台显示
     * <br>即手动在网页上改文本,注意:
     * <br>先在要翻译的文本中间写入翻译后的内容
     * <br>然后用del和backspace删除前后内容
     * <br>开启编辑模式:
     * <br>document.body.contentEditable='true';
     * <br>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;
                    }
                }
            }
        }
    };
    /*** 记录所有满足条件的未翻译内容<br>因为找不到上下文,所以一般不用*/
    noMap = {};
    /*** 用以复制value到title<br>若出现新元素,请手动通过控制台重新调用 */
    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();
}