DNF韩文职业名称翻译中文

在页面上把指定的韩文职业/角色名替换为中文

// ==UserScript==
// @name         DNF韩文职业名称翻译中文
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  在页面上把指定的韩文职业/角色名替换为中文
// @author       你
// @license MIT
// @match        https://df.nexon.com/community/news/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // ---------- 配置区 ----------
    // 若只希望在某一站点生效,可把上面的 @match 改为目标站点,或在这里设置 TARGET_HOST 为域名(如 "example.com")
    //const TARGET_HOST = null; // null = 全站生效;否则填入字符串,比如 "example.com"
    // ---------- 映射表(韩文 -> 中文) ----------
    const map = {
        "캐릭터 밸런스": "职业平衡",
        "기본 공격 및 전직 계열 스킬 공격력이": "基本攻击及转职系技能攻击力",
        "증가합니다.": "增加",
        "다크나이트": "黑暗武士",
        "트래블러": "旅人",
        "스페셜리스트": "源能专家",
        "트러블 슈터": "战线佣兵",
        "요원": "特工",
        "히트맨": "暗刃",
        "다크 랜서": "暗枪",
        "드래고니안 랜서": "狩猎者",
        "듀얼리스트": "决战者",
        "뱅가드": "征战者",
        "드래곤나이트": "龙骑士",
        "팔라딘": "帕拉丁",
        "카오스": "混沌魔灵",
        "섀도우댄서": "影舞者",
        "쿠노이치": "忍者",
        "사령술사": "黑夜术士",
        "로그": "暗星",
        "미스트리스": "魅影术师",
        "무녀": "驱魔师(巫女?)",
        "이단심판관": "异端审判者",
        "어벤저": "惩戒者",
        "퇴마사": "驱魔师",
        "인파이터": "蓝拳使者",
        "마도학자": "魔道学者",
        "배틀메이지": "战斗法师",
        "소환사": "召唤师",
        "엘레멘탈 마스터": "元素师",
        "디멘션워커": "次元行者",
        "스위프트 마스터": "风法",
        "블러드 메이지": "猩红法师",
        "빙결사": "冰洁师",
        "엘레멘탈 바머": "元素爆破师",
        "스핏파이어(여)": "弹药专家(女)",
        "메카닉(여)": "机械师(女)",
        "런처(여)": "枪炮师(女)",
        "레인저(여)": "漫游抢手(女)",
        "메카닉(남)": "机械师(男)",
        "런처(남)": "枪炮师(男)",
        "레인저(남)": "漫游抢手(男)",
        "그래플러(여)": "柔道家(女)",
        "스트라이커(여)": "散打(女)",
        "그래플러(남)": "柔道家(男)",
        "스트리트파이터(남)": "街霸(男)",
        "스트라이커(남)": "散打(男)",
        "넨마스터(남)": "气功师(男)",
        "블레이드": "刃影",
        "베가본드": "流浪武士",
        "데몬슬레이어": "契魔者",
        "소드마스터": "驭剑士",
        "검귀": "剑影",
        "버서커": "狂战士",
        "소울브링어": "鬼泣",
        "웨펀마스터": "剑魂"
    };

 const keys = Object.keys(map).sort((a,b)=>b.length-a.length).map(escapeRegExp);
    const regex = new RegExp(keys.join("|"), "g");

    function escapeRegExp(s){ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); }

    function translateTextNode(node) {
        if (!node || !node.nodeValue) return;
        const old = node.nodeValue;
        if (!node.parentNode) return;
        if (!node.parentNode.dataset) return;
        // 如果没有保存过原文,先保存
        if (!node.parentNode.dataset.originalText) {
            node.parentNode.dataset.originalText = old;
        }
        const replaced = old.replace(regex, m => map[m] ?? m);
        if (replaced !== old) node.nodeValue = replaced;
    }

    function walkAndTranslate(root) {
        const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null);
        let n;
        while (n = walker.nextNode()) {
            translateTextNode(n);
        }
    }

    function restore(root) {
        const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null);
        let n;
        while (n = walker.nextNode()) {
            const parent = n.parentNode;
            if (parent && parent.dataset && parent.dataset.originalText) {
                n.nodeValue = parent.dataset.originalText;
                delete parent.dataset.originalText;
            }
        }
    }

    // 初始翻译
    function initialTranslate() {
        walkAndTranslate(document.body);
    }

    // 添加控制按钮
    function addControlButtons() {
        const bar = document.createElement("div");
        bar.style.position = "fixed";
        bar.style.bottom = "10px";
        bar.style.left = "10px";
        bar.style.zIndex = "999999";
        bar.style.background = "rgba(0,0,0,0.6)";
        bar.style.color = "white";
        bar.style.padding = "5px";
        bar.style.borderRadius = "6px";

        const btnTranslate = document.createElement("button");
        btnTranslate.textContent = "翻译";
        btnTranslate.onclick = ()=>walkAndTranslate(document.body);

        const btnRestore = document.createElement("button");
        btnRestore.textContent = "还原";
        btnRestore.onclick = ()=>restore(document.body);

        [btnTranslate, btnRestore].forEach(b=>{
            b.style.margin = "0 5px";
            bar.appendChild(b);
        });

        document.body.appendChild(bar);
    }

    if (document.readyState === "complete" || document.readyState === "interactive") {
        initialTranslate();
        addControlButtons();
    } else {
        window.addEventListener("DOMContentLoaded", ()=>{
            initialTranslate();
            addControlButtons();
        });
    }

})();