VNDB优先原文和中文化

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

当前为 2022-06-04 提交的版本,查看 最新版本

// ==UserScript==
// @name         VNDB优先原文和中文化
// @namespace    http://tampermonkey.net/
// @version      4.2.1
// @description  优先显示原文(title->value),以及中文化(mainMap[value]->value)
// @author       aotmd
// @match        https://vndb.org/*
// @noframes
// @license MIT
// @run-at document-body
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// ==/UserScript==

/**-------------------------------数据部分[850行]-------------------------------------*/
/*通用主map,作用在全局*/
let mainMap = {
    /**左侧栏[常驻]*/
    /*菜单*/
    "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": "退出登录(不可用)",
    /*未登陆状态*/
    "User menu": "用户菜单",
    "Login": "登录(不可用)",
    "Password reset": "重置密码",
    "Register": "注册(不可用)",

    /*数据库统计*/
    "Database Statistics": "数据库统计",
    "Visual Novels": "视觉小说",

    /** 标题和,底部[常驻] */
    "the visual novel database": "视觉小说数据库",
    "about us": "关于我们",

    /** 额外map提升*/
    /*个人页相关*/
    "Arabic": "阿拉伯语",
    "Bulgarian": "保加利亚语",
    "Catalan": "加泰罗尼亚语",
    "Chinese": "中文",
    "Chinese (simplified)": "中文(简体)",
    "Chinese (traditional)": "中文(繁体)",
    "Croatian": "克罗地亚语",
    "Czech": "捷克语",
    "Danish": "丹麦语",
    "Dutch": "荷兰语",
    "English": "英语",
    "Esperanto": "世界语",
    "Finnish": "芬兰语",
    "French": "法语",
    "German": "德语",
    "Greek": "希腊语",
    "Hebrew": "希伯来语",
    "Hindi": "印地语",
    "Hungarian": "匈牙利语",
    "Indonesian": "印尼语",
    "Irish": "爱尔兰语",
    "Italian": "意大利语",
    "Japanese": "日语",
    "Korean": "韩语",
    "Latin": "拉丁语",
    "Latvian": "拉脱维亚语",
    "Lithuanian": "立陶宛语",
    "Macedonian": "马其顿语",
    "Malay": "马来语",
    "Norwegian": "挪威语",
    "Persian": "波斯语",
    "Polish": "波兰语",
    "Portuguese (Brazil)": "葡萄牙语(巴西)",
    "Portuguese (Portugal)": "葡萄牙语(葡萄牙)",
    "Romanian": "罗马尼亚语",
    "Russian": "俄语",
    "Scottish Gaelic": "苏格兰盖尔语",
    "Slovak": "斯洛伐克语",
    "Slovene": "斯洛文尼亚语",
    "Spanish": "西班牙语",
    "Swedish": "瑞典语",
    "Tagalog": "塔加洛语",
    "Thai": "泰语",
    "Turkish": "土耳其语",
    "Ukrainian": "乌克兰语",
    "Urdu": "乌尔都语",
    "Vietnamese": "越南语",

    /*评分说明*/
    "masterpiece": "杰作|超神作",
    "excellent": "极好|神作",
    "so-so": "一般般|不过不失",
    "very good": "很好|力荐",
    "good": "好|推荐",
    "decent": "不错|还行",
    "weak": "不太行|较差",
    "bad": "糟糕|差",
    "awful": "很坏|很差",
    "worst ever": "最差|不忍直视",

    "Vote stats": "评分统计",
    "Recent votes": "最近评分",
    "show all": "显示全部",

    "Report an issue on this page.": "在此页面上报告问题。",

};
/**
 * 用以替换title的值,若mainMap有则会自动替换,不需要再重复在这写一遍
 * @type {{Object}}
 */
let titleMap={

};

/** 特殊全局map,用以替换变动的文本节点[正则],
 * value出现的%%$1%%为需要继续翻译值
 * vlaue出现的%%@@$1@@%%将$1转小写,然后继续翻译值
 * */
let specialMap = {
    /*转小写再匹配map,范围太广不使用*/
    // "^([a-zA-Z -]+)$":"%%@@$1@@%%",
    /** 游戏详情页,评分统计 /v\d+ */
    "^(\\d+) vote[s]? total, average ([\\d.]+) \\(([a-zA-Z -]+)\\)$": "总共$1票, 平均分$2 (%%$3%%)",
    /** 讨论  */
    "^discussions \\((\\d+)\\)$": "讨论 ($1)",
};


/*额外map,作用在指定页面*/
let otherPageRules = [
    {
        /*作用页说明*/
        name: '个人页相关',
        /*正则表达式*/
        regular: /\/u\d+/i,
        /*mainMap k->v*/
        map: {
            /** 用户页顶栏   /ID */
            "edit": "编辑",
            "list": "列表",
            "votes": "评分",
            "wishlist": "愿望单",
            "reviews": "评论",
            "posts": "帖子",
            "history": "历史",
            /** 个人资料页   /ID */
            "Username": "用户名",
            "Registered": "注册(不可用)日期",
            "Edits": "编辑",
            "Votes": "评分",
            "Browse votes »": "浏览评分 »",
            "Play times": "游戏时间",
            "List stats": "列表统计",
            "Browse list »": "浏览列表 »",
            "Reviews": "评论",
            "Browse reviews »": "浏览评论 »",
            "Browse tags »":"浏览标签 »",
            "Images": "图片",
            "Browse image votes »": "浏览图片投票 »",
            "Forum stats": "论坛统计",
            "Browse posts »": "浏览帖子 »",
            "Vote statistics": "评分统计",
            // "Vote stats": "评分统计",
            // "Recent votes": "最近评分",
            // "show all": "显示全部",
            /** 编辑页 /ID/edit */
            "My Account": "我的账号",
            "Account settings": "账号设置",
            "change": "修改",
            "E-Mail": "电子邮箱",
            "Change password": "更改密码",
            "Preferences": "偏好",
            "NSFW": "NSFW",
            "Hide sexually suggestive or explicit images": "隐藏性暗示或色情图片",
            "Hide all images": "隐藏所有图片",
            "Hide only sexually explicit images": "只隐藏色情图片",
            "Don't hide suggestive or explicit images": "不隐藏性暗示或色情图片",
            "Hide violent or brutal images": "隐藏暴力或残暴图片",
            "Hide only brutal images": "只隐藏残暴的图片",
            "Don't hide violent or brutal images": "不隐藏暴力或残暴的图片",
            "Show sexual traits by default on character pages": "默认情况下在人物页面上显示性特征",
            "Title language": "标题语言",
            "Add language": "添加语言",
            "Original language": "原始语言",
            "romanized": "罗马化",
            "Alternative title": "副标题",
            "The alternative title is displayed below the main title and as tooltip for links.": "副标题显示在主标题下方,并作为链接的提示",
            /*语言相关已提升至主map*/
            "remove": "移除",
            "Show all tags by default on visual novel pages (don't summarize)": "在视觉小说页面上默认显示所有标签(不汇总)",
            "Default tag categories on visual novel pages:": "视觉小说页面上默认显示的标签类别:",
            "Content": "内容",
            "Sexual content": "色情内容",
            "Technical": "技术相关",
            "Spoiler level": "剧透级别",
            "Hide spoilers": "隐藏剧透",
            "Show only minor spoilers": "仅显示轻微剧透",
            "Show all spoilers": "显示所有剧透",
            "Skin": "皮肤",
            "AIR (sky blue)": "AIR(天蓝)",
            "Angelic Serenade (dark blue)": "エンジェリックセレナーデ 天使小夜曲(深蓝色)",
            "EIeL (peach-orange)": "電脳妖精エルファン (桃橙色)",
            "Eien no Aselia (falu red)": "永遠のアセリア 永远的艾塞莉娅 (法鲁红)",
            "Ever17 (bondi blue)": "ever17 (邦迪蓝)",
            "Fate/stay night (pale carmine)": "fate/stay night (淡胭脂红)",
            "Fate/stay night (seal brown)": "fate/stay night (海豹棕)",
            "Gekkou no Carnevale (black)": "月光のカルネヴァーレ 月光嘉年华(黑色)",
            "Higanbana no Saku Yoru ni (maroon)": "彼岸花の咲く夜に 彼岸花盛开之夜 (栗色)",
            "Higurashi no Naku Koro ni (orange)": "ひぐらしのなく頃に 寒蝉鸣泣之时 (橙色)",
            "Little Busters! (lemon chiffon)": "リトルバスターズ! little busters! (柠檬雪纺)",
            "Little Busters! (pink)": "リトルバスターズ! little busters! (粉色)",
            "Neon (black)": "荧光 (黑色)",
            "Primitive Link (pale chestnut)": "プリミティブ リンク primitive link (淡栗子)",
            "Saya no Uta (dark scarlet)": "沙耶の唄 沙耶之歌 (深红)",
            "Seinarukana (white)": "聖なるかな -The Spirit of Eternity Sword 2- (白色)",
            "Sora no Iro, Mizu no Iro (turquoise)": "そらのいろ、みずのいろ 空之色,水之色 (绿松石)",
            "Teal (teal)": "青色 (青色)",
            "Touhou (grey)": "东方 (灰色)",
            "Tsukihime (black)": "月姫 (黑色)",
            "Tsukihime (midnight blue)": "月姫 (午夜蓝)",
            "Custom CSS": "自定义CSS",
            "Public profile": "公开资料",
            "You can add": "您可以添加",
            "character traits": "性格特征",
            "to your account. These will be displayed on your public profile.": "到您的帐户。这些资料会公开显示。",
            "No results": "无结果",
            "Add trait...": "添加特征...",
            "Submit": "提交",
            /*在选择后出现的新文本:*/
            "Only if original title": "仅当是原始标题时",
            "Only if official title": "仅当是官方标题时",
            "Include non-official titles": "也包括非官方标题",
            "New username": "新用户名",
            "You may only change your username once a day. Your old username(s) will be displayed on your profile for a month after the change.": "您每天只能更改一次用户名。更改用户名后,旧用户名会在个人资料中显示一个月。",
            "Old password": "旧密码",
            "New password": "新密码",
            "Repeat": "重复新密码",

            /** 列表页 /ID/ulist?vnlist=1 */
            "My list": "我的列表",
            "ALL": "显示全部",
            "Voted": "已评分",
            "No label": "无标签",
            "Multi-select": "多选",
            "Update filters": "更新过滤器",
            /*标签管理*/
            "Manage labels": "标签管理",
            "How to use labels": "如何使用标签",
            "You can assign multiple labels to a visual novel": "您可以为视觉小说分配多个标签",
            "You can create custom labels or just use the built-in labels": "您可以创建自定义标签或仅使用内置标签",
            "Private labels will not be visible to other users": "其他用户看不见私有标签",
            "Your vote and notes will be public when at least one non-private label has been assigned to the visual novel": "当视觉小说分配了至少一个非私有标签时,您的评分和笔记将是公开状态",
            "VNs": "VN数量",
            "Label": "标签",
            "Private": "私有性",
            "New label": "新建标签 ",
            "Save changes": "保存更改",
            "private": "私有",
            "built-in": "内置的",
            "applied when you vote": "当你评分时更新",
            /*保存为默认值*/
            "Save as default": "保存为默认值",
            "This will change the default label selection, visible columns and table sorting options for the selected page to the currently applied settings.": "这将把所选页面的默认标签选择、可见列和排序方式更改为当前的设置。",
            "The saved view will also apply to users visiting your lists.": "保存的视图也会应用于访问您列表的用户。",
            "(If you just changed the label filters, make sure to hit \"Update filters\" before saving)": "(如果您刚刚更改了标签过滤器,请在保存默认设置之前点击\"更新过滤器\")",
            "Save": "保存",
            /*导出*/
            "Export": "导出",
            "Export your list": "导出您的列表",
            "This function will export all visual novels and releases in your list, even those marked as private (there is currently no import function, more export options may be added later).": "此功能将导出您列表中的所有视觉小说和发行版本,包括标签为私有的(目前没有导入功能,以后可能会添加更多导出选项)",
            "Download XML export.": "下载XML导出.",
            /*显示选项*/
            "display options": "显示选项",
            "Order by": "排序方式",
            "Results": "显示数量",
            "Update": "更新",
            "Visible": "可见",
            "columns": "列",
            /*排序标记*/
            "Title": "标题",
            "Vote date": "评分时间",
            "Vote": "评分",
            "Rating": "评价",
            "Labels": "标签",
            "Added": "添加时间",
            "Modified": "修改时间",
            "Start date": "开始日期",
            "Finish date": "完成日期",
            "Release date": "发布日期",
            /*标签状态*/
            "Playing": "在玩",
            "Finished": "玩过",
            "Stalled": "搁置",
            "Dropped": "抛弃",
            "Wishlist": "愿望单",
            "Blacklist": "黑名单",


            /*Opt*/
            "Opt": "选择",
            'Notes': '笔记',
            'Remove VN': '删除 VN',
            '-- add release --': '--添加版本--',
            'Add release': '添加版本',
            /*版本,状态*/
            "Obtained": "已得到",
            "Unknown": "未知",
            "Pending": "待定",
            "On loan": "外借",
            "Deleted": "已删除",
            /*翻页按钮*/
            "next ›": "下一页 ›",
            "last »": "尾页 »",
            "« first":"« 首页",
            "‹ previous":"‹ 上一页",
            /*其他动态信息*/
            "Loading releases...":"正在加载版本...",
            "Keep label": "保留标签",
            "Delete label but keep VNs in my list": "删除标签,但保留VN在我的列表中",
            "Delete label and VNs with only this label": "删除标签,也删除只有这个标签的所有VN",
            "Delete label and all VNs with this label": "删除标签,也删除带有这个标签的所有VN",
            "WARNING: ":"警告: ",
            "Your vote is still public if you assign a non-private label to the visual novel.":"如果你给视觉小说指定了非私有标签,你的评分仍然是公开的。",
            /** 评论*/
            "You have not submitted any reviews yet.": "您还没有提交任何评论。",
            /** 帖子*/
            "My posts": "我的帖子",
            "You have not posted anything on the forums yet.": "您还没有在论坛上发布任何内容。",
            /** 历史*/
            "Docs": "文档",
            "All": "全部",
            "Only changes to existing items": "仅更改的项目",
            "Only newly created items": "仅新创建的项目",
            "Only public items": "仅限公共项目",
            "Only deleted": "仅删除",
            "Only unapproved": "仅未批准",
            "Rev.": "修订版.",
            "Date": "日期",
            "User": "用户",
            "Page": "页面",
            /** 我的通知 /notifies*/
            "My notifications": "我的通知",
            "Unread notifications": "未读通知",
            "All notifications": "所有通知",
            "No notifications!": "没有通知!",
            "Settings": "设置",
            "Notify me about edits of database entries I contributed to.": "通知关于我参与的数据库条目的编辑。",
            "Notify me about replies to threads I posted in.": "通知关于我发布的主题的回复。",
            "Notify me about comments to my reviews.": "通知关于我的评论的评论。",
            "Notify me about site announcements.": "通知有关站点公告的信息。",
        },
        titleMap:{
            "This item is public": "此项是公开的",
        },
        specialMap: {
            /** 个人资料页   /ID */
            "^(.+)'s profile$": "$1 的个人资料",
            "^(\\d+)h$": "$1小时",
            "^(\\d+)m$": "$1分钟",
            "^from (\\d+) submitted play times.$": ",来自$1个游戏.",
            "^(\\d+) release[s]? of (\\d+) visual novels.$": "$1个版本,$2部视觉小说.",
            "^(\\d+) review[s]?.":"$1个评论.",
            "^(\\d+) vote[s]? on (\\d+) distinct tag[s]? and (\\d+) visual novel[s]?.":"在$2个不同标签和$3部视觉小说上投了$1票。",
            "^(\\d+) images flagged.$": "标记了$1个图片.",
            "^(\\d+) post[s]?, (\\d+) new thread[s]?.": "$1个帖子, $2个新主题.",
            /*评分统计*/
            "^(\\d+) votes, ([\\d.]+) average.$": "$1个评分, 平均$2分.",
            "^(\\d+) votes total, average ([\\d.]+)$": "$1个评分, 平均$2分",
            /*他人主页*/
            "^(\\d+)h from (\\d+) submitted play times.$": "$1小时,来自$1个游戏.",
            /** 列表页 /ID/ulist?vnlist=1 */
            /*排序头*/
            "^([a-zA-Z ]+) ▴$":"%%$1%% ▴",
            /*评分说明(下拉列表,选择分数时)*/
            "^(\\d+) \\(([a-zA-Z -]+)\\)$": "$1 (%%$2%%)",
            /** 评论*/
            "^Reviews by (.+)$": "$1的评论",
            /** 历史*/
            "^Edit history of (.+)$": "$1的编辑历史",
        },
    },
    {
        name: '登录(不可用)|注册(不可用)|重置密码',
        regular: /\/u\/(login|newpass|register)/i,
        map: {
            /*登陆页*/
            "Username": "用户名",
            "No account yet?": "还没有账号?",
            "Password": "密码",
            "Forgot your password?": "忘记密码?",
            "Submit": "提交",
            /*重置密码*/
            "E-Mail": "电子邮箱",
            "Forgot Password": "忘记密码",
            "Forgot your password and can't login to VNDB anymore?": "忘记密码,登录(不可用)不了VNDB?",
            "Don't worry! Just give us the email address you used to register on VNDB": "别担心!只需提供您在VNDB上注册(不可用)时的电子邮箱地址",
            "and we'll send you instructions to set a new password within a few minutes!": "我们将在几分钟内向您发送设置新密码的说明!",
            /*注册(不可用)账号*/
            "Create an account": "创建账号",
            "Preferred username. Must be between 2 and 15 characters long and consist entirely of alphanumeric characters or a dash.": "首选用户名。长度必须在2到15个字符之间,由字母数字或-组成。",
            "Names that look like database identifiers (i.e. a single letter followed by several numbers) are also disallowed.": "看起来像数据库标识符的名称(即一个字母后跟几个数字)也不允许使用。",
            "A valid address is required in order to activate and use your account.": "需要有效地址才能激活和使用您的帐户。",
            "Other than that, your address is only used in case you lose your password,": "除此之外,您的地址仅在您丢失密码的情况下使用,",
            "we will never send spam or newsletters unless you explicitly ask us for it or we get hacked.": "我们永远不会发送垃圾信息或时事通讯,除非您明确要求我们这样做,要不就是我们被黑客攻击了。",
            "Anti-bot question: How many visual novels do we have in the database? (Hint: look to your left)": "反机器人问题:数据库中有多少视觉小说?(提示:向左看)",
            "Answer": "回答",
        },
        titleMap:{
        },
        specialMap: {

        },
    },
    {
        name:'首页右侧主板',
        regular:/^\/$/i,
        map:{
            "The Visual Novel Database": "视觉小说数据库",
            "VNDB.org strives to be a comprehensive database for information about visual novels.": "VNDB.org致力于成为一个全面的视觉小说信息数据库。",
            "This website is built as a wiki, meaning that anyone can freely add\n                  and contribute information to the database, allowing us to create the\n                  largest, most accurate and most up-to-date visual novel database on the web.": "这个网站是作为一个维基建立的,这意味着任何人都可以自由地向数据库添加和贡献信息,这让我们能够创建网络上最大、最准确和最新的视觉小说数据库。",
            "Recent Changes": "最近更改",
            "Announcements": "公告",
            "VNDB": "VNDB",
            "DB Discussions": "数据库讨论",
            "Forums": "论坛",
            "VN Discussions": "VN讨论",
            "Latest Reviews": "最新评论",
            "Upcoming Releases": "即将发布的版本",
            "Just Released": "刚刚发布的版本",
        },
        titleMap:{
        },
        specialMap:{

        },
    },
    {
        name:'讨论板|讨论区',
        regular:/^\/t\/.+/i,
        map:{
            /** 自己的讨论*/
            "Index": "主页",
            "All boards": "全部板块",
            "VNDB discussions": "VNDB 讨论",
            "General discussions": "一般讨论",
            "Start a new thread": "创建一个新帖子",
            "An empty board": "空的板块",
            "Nobody's started a discussion on this board yet. Why not": "还没有人在这块板上开始讨论。为什么不",
            "create a new thread": "创建一个新帖子",
            "yourself?": ",由你自己?",
            /** 全部讨论*/
            "Search!": "搜索!",
            "Topic": "主题",
            "Replies": "回复",
            "Starter": "发表人",
            "Last post": "最近回复",
            "Discussion board index":"讨论区主页",
            /* 主题标题标签*/
            "[poll]":"[投票]",
            /** 创建一个新帖子*/
            "Create new thread": "创建新帖子",
            "Thread title": "帖子标题",
            "Boards": "板块",
            "You can link this thread to multiple boards. Every visual novel, producer and user in the database has its own board,": "你可以将这个帖子链接到多个版块。数据库中的每个视觉小说、制片人和用户都有自己的版块,",
            "but you can also use the \"General Discussions\" and \"VNDB Discussions\" boards for threads that do not fit at a particular database entry.": "但您也可以使用\"一般讨论\"或\"VNDB讨论\"板来处理不适合特定数据库条目的帖子。",
            "Add boards...": "添加板块...",
            "Message": "信息",
            "(English please!)": "(请用英语!)",
            "Formatting": "可用的格式代码",
            "Edit": "编辑",
            "Preview": "预览",
            "Add poll": "添加投票",
            /*一些表单提示信息*/
            "Please add at least one board.": "请添加至少一个板块。",
            "The form contains errors, please fix these before submitting.": "表单包含错误,请在提交前修复这些错误。",
            "List contains duplicates.":"列表包含重复项",
            /*投票*/
            "Poll question": "投票问题",
            "Options": "选项",
            "Add option": "添加选项",
            "Number of options people are allowed to choose.": "允许用户选择的选项数量。",
            /* 查看其他人的帖子*/
            "Posted in": "发表于",
            "report": "举报",
            "Quick reply": "快速回复",
        },
        titleMap:{

        },
        specialMap:{
            /** 自己的讨论*/
            "^Related discussions for (.+)$": "$1的相关讨论",
            /** 创建一个新帖子*/
            /*投票*/
            "^Option #(\\d+)$": "选项 #$1",

        },
    },
    {
        name:'我的标签|标签',
        regular:/^\/g\/links/i,
        map:{
            /*我的标签*/
            "Tag link browser": "标签链接浏览器",
            "Active filters:": "活动过滤器:",
            "] User:": "] 用户:",
            "No tag votes matching the requested filters.": "没有与要求的过滤器匹配的标签评分。",
            /*表头*/
            "Click the arrow before a user, tag or VN to add it as a filter.": "单击用户、标签或视觉小说之前的箭头,可以将其添加为筛选器。",
            "Tag": "标签",
            "Spoiler": "剧透",
            "Visual novel": "视觉小说",
            "Note": "笔记",
            /*剧透级别*/
            "minor spoiler": "轻微剧透",
            "no spoiler": "没有剧透",
            "major spoiler": "严重剧透",
        },
        titleMap:{},
        specialMap:{},
    },
    {
        name:'举报页面',
        regular:/^\/report/i,
        map:{
            "Submit report": "提交举报",
            "Subject": "主题",
            "Comment": "评论",
            "Your report will be forwarded to a moderator.": "您的举报将转发给版主。",
            "Keep in mind that not every report will be acted upon, we may decide that the problem you reported is still within acceptable limits.": "请记住,并非每个举报都会被处理,我们可能会认为您举报的问题仍在可接受的范围内。",
            "We generally do not provide feedback on reports, but a moderator may decide to contact you for clarification.": "我们通常不会对举报提供反馈,但版主可能会决定与您联系以进行解释。",
            "Reason": "理由",
            "-- Select --": "-- 选择 --",
            "Spam": "垃圾邮件",
            "Links to piracy or illegal content": "盗版或非法内容链接",
            "Off-topic": "与主题无关",
            "Unwelcome behavior": "不受欢迎的行为",
            "Unmarked spoilers": "没有标记剧透",
            "Other": "其他",
        },
        titleMap:{},
        specialMap:{},
    },
    {
        name:'特征页|标签页|作品详情页|用户主页',
        regular:/^\/(i|g|v\d+$|u\d+$)/i,
        map:{
            /*大类*/
            "Hair":"毛发",
            "Eyes":"眼睛",
            "Body":"身体",
            "Clothes":"服装",
            "Items":"物品",
            "Personality":"性格",
            "Role":"角色",
            "Engages in (Sexual)":"主动(性)",
            "Subject of (Sexual)":"被动(性)",
            "Engages in":"主动",
            "Subject of":"被动",
            /*细分*/
            "Sexual Content":"色情内容",
            // "ADV":"ADV",
            "Male Protagonist":"男性主角",
            "Penetrative Sex":"插入式做爱",
            "No Sexual Content":"无色情内容",
            "Student":"学生",
            "Multiple Endings":"多分支结局",
            "High Sexual Content":"大量的性爱场景",
            "Fantasy":"奇幻",
            "Romance":"恋爱",
            "Female Protagonist":"女性主角",
            "Drama":"戏剧",
            "Nukige":"拔作",
            "Non-penetrative Sex":"非插入式做爱",
            "Protagonist with a Face":"主角露过正脸",
            "Blowjob":"阴茎口交",
            "Group Sex":"群交",
            "Student Heroine":"女主角是学生",
            "Darker Sexual Contents":"更黑暗的色情内容",
            "Sexual Harassment":"性骚扰",
            "Defloration":"破处场景",
            "Rape":"强奸",
            "School":"学校",
            "Other Gameplay Elements":"其他游戏类可玩性",
            "High School Student":"高中生",
            "Fictional Beings":"虚构物种",
            "Doggy Style":"狗交式体位",
            // "BDSM":"BDSM",
            "Earth":"游戏背景地球",
            "Cowgirl":"女上位体位",
            "Big Breast Sizes Heroine":"大胸女主角",
            "Sexual Devices":"性玩具",
            "High School Student Heroine":"高中生女主角",
            "Customization":"捏人或自定义物品",
            "Comedy":"喜剧",
            "Bad Ending(s)":"坏结局",
            "Modern Day":"游戏背景现代",
            "Anal Penetration":"肛门插入",
            "Student Protagonist":"学生主角",
            "Group Sex of One Male and Several Females":"一男多女群交",
            "Linear Plot":"无分支/选项无影响",
            "Nameable Character(s)":"角色可命名",
            "Nameable Protagonist":"主角可命名",
            "Incest":"乱伦",
            "Boobjob":"乳交",
            "Missionary Position":"男上位体位",
            "Otome Game":"乙女类游戏",
            "Health Issues":"角色身体健康问题",
            "Cunnilingus":"舔穴",
            "Mystery":"悬疑",
            "Single Blowjob":"单人阴茎口交",
            "Non-human Heroine":"非人类女主角",
            "Single Ending":"单结局",
            "Outdoor Sex":"户外做爱",
            "Changing Perspective":"视角切换",
            "Modern Day Earth":"游戏背景现代地球",
            "Psychological Problems":"角色心理健康问题",
            "Handjob":"阴茎手交",
            "Organizations":"组织",
            "Bondage":"捆绑",
            "Heroine with Big Breasts":"大胸女主角",
            "Masturbation":"自慰",
            "Sex in Public Places":"公共场所做爱",
            "Group Sex of One Female and Several Males":"一女多男群交",
            "Fingering":"指交",
            "Crime":"犯罪情节",
            "Science Fiction":"科幻",
            "Voice Acting":"配音",
            "Lesbian Sex":"女性之间做爱",
            "Standing Sex":"站立式体位",
            "Naked Sprites":"裸体立绘",
            "Sitting Sex":"坐姿做爱",
            "Only a Single Heroine":"单女主角",
            "Adult Heroine":"成人女主角",
            "Loli Heroine":"萝莉女主角",
            "Sixty-nine":"69式体位",
            "Branching Plot":"分支剧情",
            "Monsters":"怪物",
            "Relationship Problems":"感情危机",
            "Adult Protagonist":"成人主角",
            "Kinetic Novel":"视觉小说(无选项)",
            "Multiple Penetration":"多重插入",
            "Vibrators":"振动棒",
            "Event CGs":"事件CG",
            "Pregnancy":"怀孕",
            "Protagonist's Sister as a Heroine":"主角的姐姐或妹妹为女主角",
            "Anal Sex":"肛交",
            "Heroine with Glasses":"眼镜娘女主角",
            "Quickie Fix Position":"站立后入",
            "Fighting Heroine":"有武力的女主角",
            "Mythical Setting":"取材自神话传说",
            "Only Virgin Heroines":"全处女主角",
            "Harem Ending":"后宫结局",
            "Brother/Sister Incest":"兄弟姐妹间做爱",
            "Protagonist's Childhood Friend as a Heroine":"幼驯染女主角",
            "Other Perspectives":"其他人视角",
            "Blood-related Incest":"近亲乱伦",
            "Modern Day Japan":"游戏背景现代日本",
            "Sex with Protagonist Only":"仅主角有性爱场景",
            "Side Portraits":"文本框旁副立绘",
            "Divine Beings":"神话生物",
            "Bukkake":"精液沐浴",
            "Violence":"暴力",
            "Twin Tail Heroine":"双马尾女主角",
            "Pregnant Sex":"孕交",
            "Immortal Heroine":"永生的女主角",
            "Jealousy":"嫉妒感情",
            "High School":"高中",
            "Tsundere Heroine":"傲娇女主角",
            "Protagonist with a Sprite":"主角有立绘",
            "High School Student Protagonist":"高中生主角",
            "Action":"动作元素",
            "Sex With Monsters":"与虚构生物做爱场景",
            "Single Boobjob":"单人乳交",
            "Bathroom Sex":"浴室做爱",
            "Urination Fetish":"排尿性爱",
            "Early Sexual Content":"游戏前期出现性内容",
            "Footjob":"足交",
            "Heroine with Sexual Experience":"有过性经验的女主角",
            "Protagonist's Younger Sister as a Heroine":"妹妹女主角",
            "Life and Death Drama":"生死攸的戏剧",
            "Lactation During Sex":"做爱时泌乳",
            "Past":"游戏背景为过去",
            "Unlockable Routes":"可解锁路线",
            "Boy x Boy Romance":"男性和男性的恋爱",
            "Sex with Tentacles":"与触手做爱",
            "Monster Rape":"怪物强奸",
            "Sex with Others":"有角色和非主角的人做爱的场景",
            "Protagonist with Voice Acting":"有配音的主角",
            "Fighting Protagonist":"主角有武力",
            "Under the Same Roof":"同居",
            "Fan-fiction":"同人小说",
            "Sounds of Copulation":"做爱的音效",
            "Male on Male Sex":"男性对男性的性行为",
            "Dark Skinned Characters":"黑皮角色",
            "Christian Mythology":"基督教神话",
            "Gender Bending":"异装/跨性别",
            "Female Ejaculation":"潮吹",
            "Ahegao":"啊嘿颜",
            "Twin Blowjob":"两人共同口交",
            "Lolicon":"萝莉性爱场景和主题",
            "Single Handjob":"单人手交",
            "Tentacle Rape":"触手强奸",
            "Vaginal Fingering":"阴道指交",
            "Map Movement":"地图移动",
            "Reverse Cowgirl":"反转女上位体位",
            "Impregnation":"受精怀孕",
            "Protagonist's Blood-related Sister as a Heroine":"实姐实妹作为女主角",
            "Intercrural Sex":"跨性别做爱",
            "Game Jam":"游戏竞赛中开发",
            "More Than Seven Endings":"多于七个结局",
            "Simulation Game":"SLG",
            "Multiple Protagonists":"可选择多主角",
            "Photographic Assets":"静态资源",
            "Leader Heroine":"领导者女主角",
            "Blood-related Brother/Sister Incest":"血亲兄弟姐妹乱伦",
            "3D Graphics":"3D图形",
            "Slice of Life":"日常片段",
            "NVL":"文本占据大部分画面",
            "Horror":"恐怖",
            "Teacher Heroine":"教师女主角",
            "Combat":"搏斗",
            "Heroine with Health Issues":"有健康问题的女主角",
            "Photographic Backgrounds":"照片背景",
            "Married Heroine":"已婚女主角",
            "Sex Industry":"性产业",
            "Demons":"恶魔",
            "Anal Toys":"肛门玩具",
            "Sexual Slavery":"性奴隶",
            "Undead":"不死生物",
            "Single Footjob":"单人足交",
            "Few Choices":"选项少",
            "Gang Bang":"轮奸",
            "Heroine with Zettai Ryouiki":"有绝对领域的女主角",
            "Kemonomimi":"兽耳",
            "Protagonist's Full Sister as a Heroine":"同父同母的妹妹女主角",
            "Adult Hero":"成年人英雄",
            "Girl x Girl Romance":"女性和女性的恋爱",
            "Unavoidable Rape":"不可避的强奸剧情",
            "Magic":"魔法",
            "Non-human Protagonist":"非人类主角",
            "Perverted Heroine":"变态女主角",
            "Fictional World":"架空世界",
            "Colored Name-tags":"姓名彩色标识",
            "Sex in Water":"水中做爱",
            "Netorare":"NTR",
            "One True End":"唯一真结局",
            "Ponytail Heroine":"马尾女主角",
            "Threesome":"3p做爱",
            "Pre-rendered 3D Graphics":"非实时渲染3D",
            "Vaginal + Anal Penetration":"双重插入",
            "Vaginal Masturbation":"阴道/阴蒂自慰",
            "Domicile":"住所里",
            "Ojousama Heroine":"大小姐女主角",
            "Divine Heroine":"神话生物女主角",
            "Protagonist with Health Issues":"有健康问题的主角",
            "Bad Endings with Story":"BadEnd有剧情",
            "University Student":"大学生",
            "Non-blood-related Incest":"非血缘乱伦",
            "Dildos":"假阴茎"
        },
        titleMap:{},
        specialMap:{
            /*匹配审批页*/
            "^([A-Za-z \(\)]+?) /$":"%%$1%% /",
        },
    },
    {
        name:'评论|他人的评论列表',
        regular:/^\/w/i,
        map:{
            /*列表页*/
            "Type": "类型",
            "Review": "评论",
            "C#":"评论",
            "Last comment": "最后评论",
            "Full": "完全",
            "Mini": "迷你",
            /*评论详情页*/
            "Was this review helpful?": "此评论对您有用吗?",
            "yes": "是",
            "no": "否",
            "Comments": "评论",
        },
        titleMap:{},
        specialMap:{
            /*评论详情页*/
            "Vote: (\\d+)": "评分: $1",
            "(\\d+) points": "$1个得分",
        },
    },
    {
        name:'封面插件翻译',
        regular:/^\/(v$|u\d+)/i,
        map:{
            /*VNDB封面插件翻译*/
            "Always Show the VN Info": "始终显示 VN 信息",
            "Show NSFW Covers": "显示 NSFW 封面",
            "Disable tooltip": "禁用工具提示",
            "Skip Additional Info": "跳过附加信息",
            "Async Cover": "异步封面",
            "Query Mode": "查询方式",
            "Legacy View": "旧版视图",
            /*封面上的文字*/
            "Status:": "状态:",
            "Release(s):": "版本:",
            "Rating:": "评价:",
            "Cast date:": "	添加时间:",
            "No English translation": "没有英文翻译",
            "Has English translation": "有英文翻译",
            "Has partial English translation": "有部分英文翻译",
            "English translation planned":"有英语翻译的计划",
            "Translation Planned.":"翻译计划完成",
            "Translation Available.":"翻译可用",
            "No Translation Available":"没有可用的翻译",
            "Length unknown.": "长度未知.",
            "[ Read more... ]": "[阅读更多...]",
        },
        titleMap:{},
        specialMap:{},
    },
    {
        name:'规则说明',
        regular:/^\//i,
        map:{},
        titleMap:{},
        specialMap:{},
    },
];

/**-----------------------------业务逻辑部分[300行]----------------------------------*/

/** ---------------------------map处理---------------------------*/
let pathname = window.location.pathname;
otherPageRules.forEach((item) => {
    //当regular是正则才执行
    if (item.regular !== undefined && item.regular instanceof RegExp) {
        if (item.regular.test(pathname)) {
            //添加到主map,若存在重复项则覆盖主map
            Object.assign(mainMap, item.map);
            //添加特殊map
            Object.assign(specialMap, item.specialMap);
            //添加titleMap
            Object.assign(titleMap, item.titleMap);
            console.log(item.name + ',规则匹配:' + pathname + '->' + item.regular);
        }
    }
});
/*object转Map, 正则new效率原因,先new出来*/
(function () {
    let tempMap = new Map();
    let k = Object.getOwnPropertyNames(specialMap);
    for (let i = 0, len = k.length; i < len; i++) {
        try {
            tempMap.set(new RegExp(k[i]), specialMap[k[i]]);
        } catch (e) {
            console.log('"' + k[i] + '"不是一个合法正则表达式');
        }
    }
    specialMap = tempMap;
})();
/** ----------------------------END----------------------------*/


/**
 * 递归节点
 * @param el   要处理的节点
 * @param func 调用的函数
 */
function 递归(el, func) {
    const nodeList = el.childNodes;
    /*先处理自己*/
    数据归一化(el,false);
    for (let i = 0; i < nodeList.length; i++) {
        const node = nodeList[i];
        数据归一化(node);
    }
    function 数据归一化(el,recursion=true) {
        if (el.nodeType === 1) {
            //为元素则递归
            if (recursion){
                递归(el, func);
            }
            let attribute, value, flag = false;
            if (el.nodeName === 'INPUT') {
                value = el.getAttribute('value');
                attribute = 'value';
                if (value == null || value.trim().length === 0) {
                    value = el.getAttribute('placeholder');
                    attribute = 'placeholder';
                }
                flag = true;
            } else if (el.nodeName === 'TEXTAREA') {
                value = el.getAttribute('placeholder');
                attribute = 'placeholder';
                flag = true;
            } else if (el.getAttribute('title')!==null&&
                el.title.length!==0) {
                /*过判断用*/
                value = 'title用过判断value值';
                attribute = 'title';
                flag = true;
            }
            if (!flag) return;
            func(el, value, attribute);
        } else if (el.nodeType === 3) {
            //为文本节点则处理数据
            func(el, el.nodeValue);
        }
    }
}
recordsList = [];
let observerMap = new Map();
/**
 * dom修改事件,包括属性,内容,节点修改
 * @param document 侦听对象
 * @param func  执行函数,可选参数(records),表示更改的节点
 */
function dom修改事件(document, func) {
    const MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;//浏览器兼容
    const config = {attributes: true, childList: true, characterData: true, subtree: true};//配置对象
    const observer = new MutationObserver(function (records, itself) {
        recordsList.push(records);
        //进入后停止侦听
        let flag = false;
        let obsArr = [];
        let selfIndex = -1;
        let doc = -1;
        //找到当前对象对应的value,和索引,以及k
        for (let key of observerMap.keys()) {
            let t = observerMap.get(key);
            for (let i = 0; i < t.length; i++) {
                if (itself === t[i][0]) {
                    obsArr = t;
                    selfIndex = i;
                    doc = key;
                    flag = true;
                    break;
                }
            }
            if (flag) {
                break;
            }
        }
        if (selfIndex === -1) {
            console.error('没有找到obs的v');
            return;
        }
        /*停止与之相同config的obs*/
        for (let i = 0; i < obsArr.length; i++) {
            if (JSON.stringify(obsArr[i][1]) === JSON.stringify(obsArr[selfIndex][1])) {
                obsArr[i][0].disconnect()
            }
        }
        /*调用与之相同config的obs*/
        try {
            for (let i = 0; i < obsArr.length; i++) {
                if (JSON.stringify(obsArr[i][1]) === JSON.stringify(obsArr[selfIndex][1])) {
                    obsArr[i][2](records);
                }
            }
        } catch (e) {
            console.error('执行错误')
        }
        //启用与之相同config的obs
        for (let i = 0; i < obsArr.length; i++) {
            if (JSON.stringify(obsArr[i][1]) === JSON.stringify(obsArr[selfIndex][1])) {
                obsArr[i][0].observe(doc, obsArr[i][1]);
            }
        }
    });
    if (observerMap.get(document) !== undefined) {
        let v = observerMap.get(document);
        v.push([observer, config, func]);
        observerMap.set(document, v);
    } else {
        observerMap.set(document, [[observer, config, func]]);
    }
    /*开始侦听*/
    observer.observe(document, config);
}

(function () {
    /*立即执行*/
    console.time('初始原文化 ,时间');
    递归(document.body, 原文化);
    console.timeEnd('初始原文化 ,时间');
    console.time('初始字典翻译,时间');
    递归(document.body, 字典翻译);
    console.timeEnd('初始字典翻译,时间');
    /*当body发生变化时执行*/
    dom修改事件(document.body, (records) => {
        console.time('原文化 ,时间');
        for (let i = 0, len = records.length; i < len; i++) {
            递归(records[i].target, 原文化);
        }
        console.timeEnd('原文化 ,时间');
        console.time('字典翻译,时间');
        for (let i = 0, len = records.length; i < len; i++) {
            递归(records[i].target, 字典翻译);
        }
        console.timeEnd('字典翻译,时间');
    });

    function 原文化(node, value, attribute = 'Text') {
        if (value == null || value.trim().length === 0) return;
        value = value.trim();

        if (attribute === 'Text') {
            let title = node.parentNode.getAttribute("title");
            if (内容判定(title, value)) {
                node.parentNode.setAttribute("title", value);
                node.nodeValue = title;
                // console.log(value+'->'+title)
            }
        } else {
            let title = node.getAttribute("title");
            if (内容判定(title, value)) {
                //若为通常节点则正常设置属性
                node.setAttribute('title', value);
                node.setAttribute(attribute, title);
                // console.log(value+'->'+title)
            }
        }

        /**
         * 显示的部分不为中文或日文,并且交换的部分为中文或日文
         * 且不应只有空格和> (标签链接浏览器页vn匹配)
         * 并且value没有对应翻译值,title没有翻译过[通过查找' \t\n'标记判断]
         * @param title
         * @param value
         * @returns {boolean}
         */
        function 内容判定(title, value) {
            return title != null
                && !/[\u4E00-\u9FA5ぁ-んァ-ヶ]+/.test(value)
                && !/^[> ]+$/.test(value)
                && /[\u4E00-\u9FA5ぁ-んァ-ヶ]+/.test(title)
                && mainMap[value]===undefined
                && title.indexOf(' \t\n')===-1;
        }
    }

    function 字典翻译(node, value, attribute = 'Text') {
        if (value == null || value.trim().length === 0) return;
        value = value.trim();
        /** titleMap翻译*/
        if (attribute==='title'){
            if(mainMap[value] === undefined
                &&node.nodeType === 1&&node.title
                ){
                /*如果为节点类型,value没有翻译,且存在title*/
                let flag=true;
                /*判断子节点文本,若文本为中文日文或匹配mainMap或与title相等,则执行后续操作*/
                let nodelist=node.childNodes;
                for (let i=0;i<nodelist.length;++i){
                    if (nodelist[i].nodeType===3&&
                        (/[\u4E00-\u9FA5ぁ-んァ-ヶ]+/.test(nodelist[i].nodeValue)
                            ||mainMap[nodelist[i].nodeValue] !== undefined
                            ||node.title===nodelist[i].nodeValue)
                    ){
                        flag=false;
                        break;
                    }
                }
                if (flag){/*加上翻译信息*/
                    if (titleMap[node.title] !== undefined) {
                        node.title=titleMap[node.title]+" \t\n"+node.title;
                    }else if (mainMap[node.title]!==undefined){
                        node.title=mainMap[node.title]+" \t\n"+node.title;
                    }
                }
            }
            return;
        }
        /** mainMap翻译*/
        if (mainMap[value] !== undefined) {
            if (attribute === 'Text') {
                //若为文本节点则追加父节点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 = mainMap[value];
            } else {
                //若为通常节点则正常设置属性
                node.setAttribute('title', value);
                node.setAttribute(attribute, mainMap[value])
            }
        }else {
            /** specialMap正则翻译*/
            //遍历specialMap,正则替换
            for (let key of specialMap.keys()) {
                /*正则匹配*/
                if (key.test(value)) {
                    /*正则替换*/
                    let newValue = value.replace(key, specialMap.get(key));

                    /*若有循环替换符,则进行替换*/
                    let nvs = newValue.split('%%');

                    /*如果map的值没有中文,且带%%%%,则设置flag为true*/
                    let flag = false;
                    if (!/[\u4E00-\u9FA5]+/.test(specialMap.get(key))
                        && nvs.length !== 1 && nvs.length % 2 === 1) {
                        flag = true;
                    }
                    if (nvs.length !== 1 && nvs.length % 2 === 1) {
                        for (let i = 1; i < nvs.length; i += 2) {
                            /*转小写*/
                            let low = nvs[i].split('@@');
                            if (low.length === 3) {
                                nvs[i] = low[1].toLowerCase();
                            }
                            /*匹配mainMap*/
                            if (mainMap[nvs[i]] !== undefined) {
                                nvs[i] = mainMap[nvs[i]];
                                /*若找到map,则重新置flag为false*/
                                flag = false;
                            }
                        }
                        newValue = nvs.join('')
                    }
                    if (flag) {/*如果替换式没有中文,且%%%%也没有匹配,则跳过*/
                        continue;
                    }
                    if (attribute === 'Text') {
                        //若为文本节点则追加父节点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 = newValue;
                    } else {
                        //若为通常节点则正常设置属性
                        node.setAttribute('title', value);
                        node.setAttribute(attribute, newValue)
                    }
                    // console.log(value+'->'+newValue);
                    /*替换后结束遍历*/
                    break;
                }
            }
        }
    }
})();


/**-----------------------------开发用函数部分[350行]----------------------------------*/
/** 开启后通过控制台调用函数即可*/
let 开发者模式 = false;
if (开发者模式) {
    /*exportMap已弃用*/
    /**
     * 导出新的已被翻译的内容到控制台显示
     * <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 = 'Text') {
            if (value == null || value.trim().length === 0) return;
            value = value.trim();
            //没有在map中找到翻译
            if (mainMap[value] === undefined) {
                //是中文、不是日文
                if (/[\u4E00-\u9FA5]+/.test(value) &&
                    !/[ぁ-んァ-ヶ]+/.test(value)) {
                    if (attribute === 'Text') {
                        node = node.parentNode;
                    }
                    let title = node.getAttribute('title');
                    //如果title没有翻译,则记录
                    if (title !== null && mainMap[title] === undefined) {
                        addMap[title] = value;
                    }
                }
            }
        }
    };
    /*** 记录所有满足条件的未翻译内容<br>缺点为找不到上下文*/
    noMap = {};
    /*** 记录所有满足条件的未翻译提示信息<br>缺点为找不到上下文*/
    noTitleMap={};
    /*** <s>用以复制value到title[已弃用]</s>
     * <br>现用来导出未翻译的title和value,map->控制台
     * <br>若出现新元素,请手动通过控制台重新调用
     * <br>若干扰项太多,可以通过删除干扰元素,再重新调用
     * */
    copyToTitle = () => {
        //清空
        noMap = {};
        noTitleMap={};
        递归(document.body, 数据处理);
        console.log(JSON.stringify(noMap));
        console.log(JSON.stringify(noTitleMap));

        function 数据处理(node, value, attribute = 'Text') {
            if (value == null || value.trim().length === 0) return;
            value = value.trim();
            //没有在map中找到翻译
            if (mainMap[value] === undefined) {
                //1<长度<300,不为中文、日文,不是纯数字
                if (1 < value.length && value.length < 300
                    && !/[\u4E00-\u9FA5ぁ-んァ-ヶ]+/.test(value)
                    && !/^[\d]+$/.test(value)) {
                    //归一化处理
                    if (attribute === 'Text') {
                        node = node.parentNode;
                    }
                    let title = node.getAttribute('title');
                    //title属性为中文或日文时不执行后续操作
                    if (title != null && /[\u4E00-\u9FA5ぁ-んァ-ヶ]+/.test(title)) {
                        return;
                    }
                    //未翻译的节点title
                    if(title!= null&&titleMap[title]===undefined){
                        let flag=true;
                        /*判断子节点文本,若文本为中文日文或匹配mainMap或与title相等,则不添加到未翻译title*/
                        let nodelist=node.childNodes;
                        for (let i=0;i<nodelist.length;++i){
                            if (nodelist[i].nodeType===3&&
                                (/[\u4E00-\u9FA5ぁ-んァ-ヶ]+/.test(nodelist[i].nodeValue)
                                ||mainMap[nodelist[i].nodeValue] !== undefined
                                ||title===nodelist[i].nodeValue)
                            ){
                                flag=false;
                                break;
                            }
                        }
                        /*如果存在title,且title没有定义在mainMap*/
                        /*那么添加到待翻译title信息*/
                        if (flag){
                            noTitleMap[title] = title.toLowerCase();
                        }
                    }
                    //复制value->title
                    // if (title != null && title.trim() !== value) {
                    //     node.setAttribute('title', title + ' ' + value);
                    // } else {
                    //     node.setAttribute('title', value);
                    // }
                    //设置没有翻译的map标记
                    noMap[value] = value.toLowerCase();
                }
            }
        }
    };
    /*立即执行*/
    copyToTitle();
    /**
     *统计不应该匹配,但可以匹配的k->v与正则,用以将局部map升级到主map
     * @type {{Object}}
     */
    otherLog = GM_getValue('otherLog') || {};
    console.log(otherLog);
    delotherLog = () => {
        GM_deleteValue('otherLog');
    };
    /**
     * 按降序显示otherLog数组
     */
    showotherLog=()=>{
        //复制一份
        let temp=JSON.parse(JSON.stringify(otherLog));
        let k = Object.getOwnPropertyNames(temp);
        let otherLogList=[];
        for (let i = 0, len = k.length; i < len; i++) {
            temp[k[i]].unshift(k[i]);
            otherLogList.push(temp[k[i]]);
        }
        /*排序*/
        otherLogList.sort(function (obj1, obj2) {
            return obj2[1] - obj1[1];
        });
        console.log(otherLogList);

        let sb='匹配项\t匹配数\t匹配时机\t匹配结果\n';
        for (let i=0,len=otherLogList.length;i<len;i++){
            sb+=otherLogList[i].join('\t')+'\n'
        }
        console.log(sb)
    };
    /*未生效规则匹配测试,用以筛选常用规则手动上移至主规则*/
    (() => {
        /*object转Map,将其他没有生效的map合起来*/
        let otherMap = {};
        let otherTitleMap={};
        let otherSpecialMap = new Map();
        otherPageRules.forEach((item) => {
            let pathname = window.location.pathname;
            if (item.regular !== undefined && item.regular instanceof RegExp && !item.regular.test(pathname)) {
                let k = Object.getOwnPropertyNames(item.specialMap);
                for (let i = 0, len = k.length; i < len; i++) {
                    try {
                        otherSpecialMap.set(new RegExp(k[i]), item.specialMap[k[i]]);
                    } catch (e) {
                        console.log('"' + k[i] + '"不是一个合法正则表达式');
                    }
                }
                Object.assign(otherMap, item.map);
                Object.assign(otherTitleMap, item.titleMap);
            }
        });
        /*立即执行*/
        console.time('初始其他规则,调试');
        递归(document.body, 未生效规则匹配测试);
        console.timeEnd('初始其他规则,调试');
        /*当body发生变化时执行*/
        dom修改事件(document.body, (records) => {
            console.time('其他规则,调试');
            for (let i = 0, len = records.length; i < len; i++) {
                递归(records[i].target, 未生效规则匹配测试);
            }
            console.timeEnd('其他规则,调试');
            /*若不相等则更新并输出*/
            if (JSON.stringify(otherLog) !== JSON.stringify(GM_getValue('otherLog') || {})) {
                GM_setValue('otherLog', otherLog);
                console.log(otherLog);
            }
        });

        /**
         * 统计不应该匹配,但可以匹配的k->v与正则,用以将局部map升级到主map
         * @param key
         * @param value
         */
        function otherLogAdd(key, value) {
            if (otherLog[key] === undefined) {
                otherLog[key] = [1, value[0], value[1]];
            } else {
                let item = otherLog[key];
                item[0]++;
                /*去重*/
                let a1 = item[1].split('$$');
                a1.push(value[0]);
                let mySet = new Set(a1);
                a1 = [...mySet];
                item[1] = a1.join('$$');

                a1 = item[2].split('$$');
                a1.push(value[1]);
                mySet = new Set(a1);
                a1 = [...mySet];
                item[2] = a1.join('$$');
            }
        }

        function 未生效规则匹配测试(node, value, attribute = 'Text') {
            if (value == null || value.trim().length === 0) return;
            value = value.trim();
            /*不被mainMap和specialMap匹配*/
            /*由于执行顺序的原因,该判断基本没有意义*/
            if (mainMap[value] !== undefined) {
                return;
            }
            /*因为正则涉及匹配可能太广,所以不排除*/
            // for (let key of specialMap.keys()) {
            //     if (key.test(value)) {
            //         return;
            //     }
            // }

            /*1<长度<300,不为中文、日文,不是纯数字,降低缩进*/
            let f = true;
            if (1 < value.length && value.length < 300
                && !/[\u4E00-\u9FA5ぁ-んァ-ヶ]+/.test(value)
                && !/^[\d]+$/.test(value)) {
                f = false;
            }
            if (f) return;
            /*title翻译*/
            /** titleMap翻译*/
            if (attribute==='title'){
                if(otherMap[value] === undefined
                    &&node.nodeType === 1&&node.title
                ){
                    /*如果为节点类型,value没有翻译,且存在title*/
                    let flag=true;
                    /*判断子节点文本,若文本为中文日文或匹配mainMap或与title相等,则执行后续操作*/
                    let nodelist=node.childNodes;
                    for (let i=0;i<nodelist.length;++i){
                        if (nodelist[i].nodeType===3&&
                            (/[\u4E00-\u9FA5ぁ-んァ-ヶ]+/.test(nodelist[i].nodeValue)
                                ||otherMap[nodelist[i].nodeValue] !== undefined
                                ||node.title===nodelist[i].nodeValue)
                        ){
                            flag=false;
                            break;
                        }
                    }
                    if (flag){/*加上翻译信息*/
                        let title=node.title;
                        if (otherTitleMap[title] !== undefined) {
                            node.title=otherTitleMap[title]+" \t\n"+title;
                            otherLogAdd(title, ['otherTitleMap匹配', otherTitleMap[title]]);
                        }else if (otherMap[node.title]!==undefined){
                            node.title=otherMap[title]+" \t\n"+title;
                            otherLogAdd(title, ['otherTitleMap->otherMap匹配', otherMap[title]]);
                        }
                    }
                }
                return;
            }


            if (otherMap[value] !== undefined) {
                if (attribute === 'Text') {
                    //若为文本节点则追加父节点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 = otherMap[value];
                    otherLogAdd(value, ['otherMap匹配,Text', otherMap[value]]);

                } else {
                    //若为通常节点则正常设置属性
                    node.setAttribute('title', value);
                    node.setAttribute(attribute, otherMap[value]);
                    otherLogAdd(value, ['otherMap匹配,节点', otherMap[value]]);
                }
            } else {
                //遍历specialMap,正则替换
                for (let key of otherSpecialMap.keys()) {
                    /*正则匹配,降低缩进*/
                    if (!key.test(value)) {continue;}
                    let info = 'otherSpecialMap匹配,正则:' + key + ',';
                    /*正则替换*/
                    let newValue = value.replace(key, otherSpecialMap.get(key));
                    /*若有循环替换符,则进行替换*/
                    let nvs = newValue.split('%%');
                    /*如果map的值没有中文,且带%%%%,则设置flag为true*/
                    let flag = false;
                    if (!/[\u4E00-\u9FA5]+/.test(otherSpecialMap.get(key))
                        && nvs.length !== 1 && nvs.length % 2 === 1) {
                        flag = true;
                    }
                    if (nvs.length !== 1 && nvs.length % 2 === 1) {
                        for (let i = 1; i < nvs.length; i += 2) {
                            /*转小写*/
                            let low = nvs[i].split('@@');
                            if (low.length === 3) {
                                nvs[i] = low[1].toLowerCase();
                            }
                            /*匹配otherMap*/
                            if (otherMap[nvs[i]] !== undefined) {
                                nvs[i] = otherMap[nvs[i]];
                                info += '在otherMap找到%%%%(额外匹配),';
                                /*若找到map,则重新置flag为false*/
                                flag = false;
                            }
                            /*匹配匹配mainMap*/
                            if (mainMap[nvs[i]] !== undefined) {
                                nvs[i] = mainMap[nvs[i]];
                                info += '在mainMap找到%%%%(额外匹配),';
                                /*若找到map,则重新置flag为false*/
                                flag = false;
                            }
                        }
                        newValue = nvs.join('')
                    }
                    /*如果替换式没有中文,且%%%%也没有匹配,则跳过*/
                    if (flag) {continue;}
                    if (attribute === 'Text') {
                        //若为文本节点则追加父节点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 = newValue;
                        info += 'Text';
                    } else {
                        //若为通常节点则正常设置属性
                        node.setAttribute('title', value);
                        node.setAttribute(attribute, newValue);
                        info += '节点';
                    }
                    otherLogAdd(value, [info, newValue]);
                    // console.log(value + '->' + newValue);
                    /*替换后结束遍历*/
                    break;
                }
            }
        }
    })();
}

QingJ © 2025

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