BIS 표 한글화

스탯과 장비.. 기타 등등을 한국어로 보여줌

当前为 2025-04-22 提交的版本,查看 最新版本

// ==UserScript==
// @name         BIS 표 한글화
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  스탯과 장비.. 기타 등등을 한국어로 보여줌
// @author       ㅇㅇ
// @match        https://etro.gg/*
// @match        https://xivgear.app/*
// @grant        GM_addStyle
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    GM_addStyle(`
        * {
            word-break: keep-all !important;
        }

        span, div, td, th, p {
            white-space: nowrap !important;
        }

        .stat, .stat-name, .value, .materia, .materia-name, .gear-stat, .gear-line {
            display: inline-block !important;
            white-space: nowrap !important;
            word-break: keep-all !important;
            vertical-align: middle;
            max-width: none !important;
        }

        .stat-line, .materia-line {
            flex-wrap: nowrap !important;
        }

        materia-totals-display {
            display: flex !important;
            flex-direction: column !important;
            align-items: center !important;
            text-align: center !important;
            margin-bottom: 1em;
        }

        materia-totals-display > .materia-totals-label {
            margin-bottom: 4px;
            font-weight: bold;
        }

        materia-count-display {
            display: block !important;
            margin-top: 4px;
        }

        .materia-line-wrapper {
            display: flex;
            align-items: center;
            gap: 6px;
            margin-bottom: 4px;
        }

        .materia-name-quantity {
            font-weight: bold;
        }

        .materia-stat-block {
            display: flex;
            align-items: center;
            gap: 6px;
        }
        slot-materia-manager {
            margin-right: 16px;
        }
        food-stat-bonus span {
          line-height: 1;
        }
}
}
    `);

    const wordReplacements = {
      //기타
      "Alternative Items": "대체 가능 장비",

      //스탯
      "STR": "힘","Strength": "힘",
      "DEX": "민첩","Dexterity": "민첩",
      "MDEF": "마법 방어",
      "DEF": "물리 방어",
      "VIT": "활력","Vitality": "활력",
      "INT": "지능","Intelligence": "지능",
      "MND": "정신력","Mind": "정신력",
      "CRT": "극대화",
      "DET": "의지력",
      "DHT": "직격","DH": "직격",
      "SkS": "기시속",
      "SpS": "마시속",
      "VIT": "활력","Vitality": "활력",
      "TNC": "불굴","Tenacity": "불굴",
      "PIE": "신앙","Piety": "신앙",
      "GCD": "글쿨",

      //장비 종류
      "Weapon:": "무기:",
      "Head:": "머리:",
      "Body:": "몸통:",
      "Hand:": "손:",
      "Legs:": "다리:",
      "Feet:": "발:",
      "Ears:": "귀:",
      "Neck:": "목:",
      "Wrist:": "손목:",
      "Left Ring:": "왼쪽 손가락:",
      "Right Ring:": "오른쪽 손가락:",
      "Tome": "석판 장비",
      " Raid": " 레이드 장비",

      //방어구, 장신구

      "Augmented": "보강된","Aug.": "보강된",
      "Quetzalli": "케츠할리",
      "Dark Horse Champion's": "다크호스 챔피언",
      "Ultimate Edenmorn": "절 에덴의 아침",

      //케츠할리 방어구
      "Helm of Fending": "수호자 투구","Bandana of Fending": "수호자 두건",
      "Mail of Fending": "수호자 갑옷","Coat of Fending": "수호자 외투",
      "Gauntlets of Fending": "수호자 건틀릿","Halfgloves of Fending": "수호자 손등장갑",
      "Brais of Fending": "수호자 바지",
      "Sollerets of Fending": "수호자 쇠구두","Greaves of Fending": "수호자 갑주장화",

      "Helm of Maiming": "학살자 투구","Bandana of Maiming": "학살자 두건",
      "Mail of Maiming": "학살자 갑옷","Coat of Maiming": "학살자 외투",
      "Gauntlets of Maiming": "학살자 건틀릿","Halfgloves of Maiming": "학살자 손등장갑",
      "Brais of Maiming": "학살자 바지","Breeches of Maiming": "학살자 바지",
      "Sollerets of Maiming": "학살자 쇠구두","Greaves of Maiming": "학살자 갑주장화",

      "Visor of Striking": "타격대 얼굴가리개","Hood of Striking": "타격대 후드",
      "Jacket of Striking": "타격대 재킷","Coat of Striking": "타격대 외투",
      "Vambraces of Striking": "타격대 완갑","Gloves of Striking": "타격대 장갑",
      "Breeches of Striking": "타격대 바지","Brais of Striking": "타격대 바지",
      "Leggings of Striking": "타격대 다리보호대","Boots of Striking": "타격대 장화",

      "Visor of Aiming": "유격대 얼굴가리개","Mask of Aiming": "유격대 가면",
      "Jacket of Aiming": "유격대 재킷",
      "Vambraces of Aiming": "유격대 완갑","Gloves of Aiming": "유격대 장갑",
      "Breeches of Aiming": "유격대 바지","Brais of Aiming": "유격대 바지",
      "Leggings of Aiming": "유격대 다리보호대","Boots of Aiming": "유격대 장화",

      "Visor of Scouting": "정찰대 얼굴가리개","Hood of Scouting": "정찰대 후드",
      "Jacket of Scouting": "정찰대 재킷","Coat of Scouting": "정찰대 외투",
      "Vambraces of Scouting": "정찰대 완갑","Gloves of Scouting": "정찰대 장갑",
      "Breeches of Scouting": "정찰대 바지","Brais of Scouting": "정찰대 바지",
      "Leggings of Scouting": "정찰대 다리보호대","Boots of Scouting": "정찰대 장화",

      "Hood of Healing": "치유사 후드","Hat of Healing": "치유사 모자",
      "Robe of Healing": "치유사 로브","Coat of Healing": "치유사 외투",
      "Gloves of Healing": "치유사 장갑","Halfgloves of Healing": "치유사 손등장갑",
      "Hose of Healing": "치유사 기마바지","Brais of Healing": "치유사 바지",
      "Shoes of Healing": "치유사 신발",

      "Hood of Casting": "마술사 후드","Hat of Casting": "마술사 모자",
      "Robe of Casting": "마술사 로브","Coat of Casting": "마술사 외투",
      "Gloves of Casting": "마술사 장갑","Halfgloves of Casting": "마술사 손등장갑",
      "Hose of Casting": "마술사 기마바지","Brais of Casting": "마술사 바지",
      "Shoes of Casting": "마술사 신발","Shoes of Casting": "마술사 신발",

      //케츠할리 장신구
      "Ear Cuffs of Fending": "수호자 귀찌","Earring of Fending": "수호자 귀걸이",
      "Ear Cuffs of Slaying": "공격대 귀찌","Earring of Slaying": "공격대 귀걸이",
      "Ear Cuffs of Aiming": "유격대 귀찌","Earring of Aiming": "유격대 귀걸이",
      "Ear Cuffs of Healing": "치유사 귀찌","Earring of Healing": "치유사 귀걸이",
      "Ear Cuffs of Casting": "마술사 귀찌","Earring of Casting": "마술사 귀걸이",
      "Necklace of Fending": "수호자 목걸이","Choker of Fending": "수호자 목장식",
      "Necklace of Slaying": "공격대 목걸이","Choker of Slaying": "공격대 목장식",
      "Necklace of Aiming": "유격대 목걸이","Choker of Aiming": "유격대 목장식",
      "Necklace of Healing": "치유사 목걸이","Choker of Healing": "치유사 목장식",
      "Necklace of Casting": "마술사 목걸이","Choker of Casting": "마술사 목장식",
      "Bracelets of Fending": "수호자 팔찌","Bangle of Fending": "수호자 장식고리",
      "Bracelets of Slaying": "공격대 팔찌","Bangle of Slaying": "공격대 장식고리",
      "Bracelets of Aiming": "유격대 팔찌","Bangle of Aiming": "유격대 장식고리",
      "Bracelets of Healing": "치유사 팔찌","Bangle of Healing": "치유사 장식고리",
      "Bracelets of Casting": "마술사 팔찌","Bangle of Casting": "마술사 장식고리",
      "Ring of Fending": "수호자 반지",
      "Ring of Slaying": "공격대 반지",
      "Ring of Aiming": "유격대 반지",
      "Ring of Healing": "치유사 반지",
      "Ring of Casting": "마술사 반지",

      //무기
      "Longsword": "롱소드","Sword": "한손검",
      "Patas": "파타","Jamadhars": "자마다르",
      "Bardiche": "긴날도끼","Labrys": "양날도끼",
      "Spear": "창","대체": "대체",
      "Longbow": "장궁",
      "Cane": "환술봉",
      "Rod": "주술봉",
      "Chronicle": "연대기","Index": "금서",
      "Counsel": "상담록","Codex": "치유서",
      "Knives": "단검",
      "Greatsword": "그레이트소드",
      "Handgonne": "권총","Musketoon": "단총",
      "Star Globe": "천구의","Astrometer": "천체광도계",
      "Samurai Blade": "외날검","Blade": "외날검",
      "Rapier": "레이피어","Foil": "플뢰레",
      "Bayonet": "총검","Gunblade": "건블레이드",
      "Chakrams": "차크람","Tathlums": "타흘룸",
      "War Scythe": "전투낫",
      "Wings": "날개","Milpreves": "밀프레베",
      "Twinfangs": "쌍송곳니",
      "Round Brush": "둥근붓",
      "Kite Shield": "연모양 방패",


      //마테리아
      "Quicktongue Materia": "시전의",
      "Quickarm Materia": "신속의",
      "Craftsman's Command Materia": "거장의",
      "Craftsman's Cunning Materia": "명인의",
      "Craftsman's Competence Materia": "장인의",
      "Gatherer's Grasp Materia": "기량의",
      "Gatherer's Guile Materia": "박식의",
      "Gatherer's Guerdon Materia": "달견의",
      "Battledance Materia": "강유의",
      "Savage Might Materia": "야망의",
      "Savage Aim Materia": "무략의",
      "Heavens' Eye Materia": "심안의",
      "Piety Materia": "신앙의",
      "Mind Materia": "정신력의",
      "Intelligence Materia": "지능의",
      "Dexterity Materia": "민첩성의",
      "Vitality Materia": "활력의",
      "Strength Materia": "힘의",

      " XII": " 하이알테마마테리쟈",
      " XI": " 하이오메가마테리쟈",
      " X": " 알테마마테리쟈",
      " IX": " 오메가마테리쟈",
      " VIII": " 엑스마테리쟈",
      " VII": " 메가마테리쟈",
      " VI": " 하이마테리쟈",
      " V": " 마테리쟈",
      " IV": " 마테리가",
      " III": " 마테리다",
      " II": " 마테리라",
      " I": " 마테리아",

      //음식
      "Food": "음식",
      "Moqueca": "무케카",
      "Roast Chicken": "로스트 치킨",
      "Churrasco": "슈하스코",
      "Pineapple Orange Jelly": "파인애플 오렌지 젤리",
      "Navel Orange Cookies": "배꼽 오렌지 쿠키",

      //기타2
      "Totals:": "",
      "No materia slots on this item": "마테리아 슬롯 없음",
      "max": "최대",
      "alt items": " 대체 가능 장비",
      "The item ": "아이템 '",
      " can be replaced by all of the following items, which have equivalent or better effective stats:": "'은(는) 아래의 장비로 대체 가능 합니다.",
      "iLv": "아이템 레벨",
      "Name": "이름",
      "Source": "얻는 곳",
      "Raid": "레이드",
      "Crafted": "제작",
      "Unknown": "알 수 없음",
      "Ultimate": "절 레이드",
    };

    function replaceText() {
        const walk = document.createTreeWalker(
            document.body,
            NodeFilter.SHOW_TEXT,
            null,
            false
        );

        let node;
        while ((node = walk.nextNode())) {
            let newText = node.nodeValue;

            newText = newText.replace(/\+(\d+)\s+([가-힣]+)/g, (_, num, stat) => `${stat} +${num}`);

            for (const [original, replacement] of Object.entries(wordReplacements)) {
                newText = newText.replace(new RegExp(original, 'g'), replacement);
            }

            if (newText !== node.nodeValue) {
                node.nodeValue = newText;
            }
        }
    }

    function updateMateriaLabels() {
        const materiaDisplays = document.querySelectorAll('materia-count-display');

        materiaDisplays.forEach(display => {
            if (display.getAttribute('data-updated') === 'true') return;

            const title = display.getAttribute('title');
            if (!title) return;

            const nameMatch = title.match(/^(.+?)\s*:/);
            const statMatch = title.match(/:\s*\+(\d+)\s+(.+)$/);
            if (!nameMatch || !statMatch) return;

            const materiaName = nameMatch[1];
            const statValue = statMatch[1];
            const statNameOriginal = statMatch[2];

            const quantityElem = display.querySelector('.materia-count-quantity');
            const materiaView = display.querySelector('single-materia-view-only');
            if (!quantityElem || !materiaView) return;

            const quantityText = quantityElem.textContent.match(/\d+/)?.[0];
            if (!quantityText) return;

            const imageHolder = materiaView.querySelector('.materia-image-holder');
            const statSpanInView = materiaView.querySelector('span');
            const statText = statSpanInView?.textContent || `${statNameOriginal} +${statValue}`;

            display.innerHTML = '';

            const wrapper = document.createElement('div');
            wrapper.className = 'materia-line-wrapper';

            const nameQuantitySpan = document.createElement('span');
            nameQuantitySpan.className = 'materia-name-quantity';
            nameQuantitySpan.textContent = `${materiaName} - ${quantityText}개`;

            const statBlock = document.createElement('div');
            statBlock.className = 'materia-stat-block';

            if (materiaView && imageHolder) {
                const clonedView = materiaView.cloneNode(true);
                const holders = clonedView.querySelectorAll('.materia-image-holder');
                const spans = clonedView.querySelectorAll('span');

                holders.forEach((el, idx) => {
                    if (idx > 0) el.remove();
                });

                spans.forEach((el, idx) => {
                    if (idx > 0) el.remove();
                });

                statBlock.appendChild(clonedView);
            }

            wrapper.appendChild(nameQuantitySpan);
            wrapper.appendChild(statBlock);
            display.appendChild(wrapper);
            display.setAttribute('data-updated', 'true');
        });
    }

    window.addEventListener('load', () => {
        replaceText();
        updateMateriaLabels();

        const observer = new MutationObserver(() => {
            replaceText();
            updateMateriaLabels();
        });

        observer.observe(document.body, {
            childList: true,
            subtree: true,
            characterData: true
        });
    });
})();