Greasy Fork

Greasy Fork is available in English.

FUT攻略站游戏英文名词翻译

在常用的FC UT攻略网站 fut.gg、futbin.com 和 easysbc.io 上自动为指定名词添加中文翻译

当前为 2025-11-17 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         FUT攻略站游戏英文名词翻译
// @namespace    http://tampermonkey.net/
// @version      0.75
// @description  在常用的FC UT攻略网站 fut.gg、futbin.com 和 easysbc.io 上自动为指定名词添加中文翻译
// @author       (开发)御羽卓一 yuyuzhuoyi & (翻译)FC冰红茶
// @match        https://fut.gg/*
// @match        https://www.fut.gg/*
// @match        https://www.futbin.com/*
// @match        https://futbin.com/*
// @match        https://www.easysbc.io/*
// @match        https://easysbc.io/*
// @match        https://www.ea.com/ea-sports-fc/ultimate-team/web-app/*
// @grant        none
// @license      CC BY-NC 4.0
// ==/UserScript==

(function() {
    'use strict';

    // ==================== 翻译词典配置 ====================
    // 版本 0.3:支持专业术语和玩家常用说法两种模式

    // 翻译模式开关:true = 使用玩家常用说法,false = 使用专业术语
    const usePlayerSlang = true; // 修改此值为 true 可切换为玩家常用说法

    // 翻译词典:每个词条包含专业术语和玩家常用说法两种翻译
    const translationDict = {
        // 化学
        'Wall': {
            professional: '铜墙铁壁',
            playerSlang: '铜墙铁壁'
        },
        'Glove': {
            professional: '手套',
            playerSlang: '手套'
        },
        'Shield': {
            professional: '盾',
            playerSlang: '盾'
        },
        'Cat': {
            professional: '猫',
            playerSlang: '猫'
        },
        'Anchor': {
            professional: '中流砥柱',
            playerSlang: '锚'
        },
        'Architect': {
            professional: '建筑师',
            playerSlang: '建筑师'
        },
        'Artist': {
            professional: '艺术家',
            playerSlang: '艺术家'
        },
        'Backbone': {
            professional: '主心骨',
            playerSlang: '主心骨'
        },
        'Basic': {
            professional: '基础',
            playerSlang: '基础'
        },
        'Catalyst': {
            professional: '催化剂',
            playerSlang: '催化剂'
        },
        'Deadeye': {
            professional: '神枪手',
            playerSlang: '死眼'
        },
        'Engine': {
            professional: '发动机',
            playerSlang: '引擎'
        },
        'Finisher': {
            professional: '终结者',
            playerSlang: '终结者'
        },
        'Gladiator': {
            professional: '角斗士',
            playerSlang: '角斗士'
        },
        'Guardian': {
            professional: '守护者',
            playerSlang: '守护者'
        },
        'Hawk': {
            professional: '鹰',
            playerSlang: '鹰'
        },
        'Hunter': {
            professional: '猎手',
            playerSlang: '猎手'
        },
        'Maestro': {
            professional: '大师',
            playerSlang: '大师'
        },
        'Marksman': {
            professional: '神射手',
            playerSlang: '神射手'
        },
        'Powerhouse': {
            professional: '发动机',
            playerSlang: '发动机'
        },
        'Sentinel': {
            professional: '哨兵',
            playerSlang: '哨兵'
        },
        'Shadow': {
            professional: '影子',
            playerSlang: '影子'
        },
        'Sniper': {
            professional: '狙击手',
            playerSlang: '狙击手'
        },

        // 角色
        'Box-To-Box': {
            professional: '全能中场',
            playerSlang: 'B2B'
        },
        'Advanced Forward': {
            professional: '突前型前锋',
            playerSlang: '突前型前锋'
        },
        'Poacher': {
            professional: '禁区之王',
            playerSlang: '禁区之王'
        },
        'Target Forward': {
            professional: '柱式前锋',
            playerSlang: '站桩前锋'
        },

        // 比赛风格/技能
        'Finesse Shot': {
            professional: '推射',
            playerSlang: '搓射'
        },
        'Chip Shot': {
            professional: '吊射',
            playerSlang: '吊射'
        },
        'Power Shot': {
            professional: '大力射门',
            playerSlang: '大力射门'
        },
        'Dead Ball': {
            professional: '死球',
            playerSlang: '定位球射门'
        },
        'Precision Header': {
            professional: '精准头球',
            playerSlang: '头球射门'
        },
        'Acrobatic': {
            professional: '杂耍',
            playerSlang: '花式射门'
        },
        'Low Driven Shot': {
            professional: '大力低射',
            playerSlang: '低射'
        },
        'Game Changer': {
            professional: '颠覆者',
            playerSlang: '外脚背射门'
        },
        'Gamechanger': {
            professional: '颠覆者',
            playerSlang: '外脚背射门'
        },
        'Whipped Pass': {
            professional: '弧线传中',
            playerSlang: '弧线传中'
        },
        'Inventive': {
            professional: '别出心裁(独辟蹊径)',
            playerSlang: '花哨传球'
        },
        'Tiki Taka': {
            professional: 'Tiki Taka',
            playerSlang: 'Tiki Taka'
        },
        'Long Ball Pass': {
            professional: '远距离传球',
            playerSlang: '长传'
        },
        'Pinged Pass': {
            professional: '大力传球',
            playerSlang: '大力传球'
        },
        'Incisive Pass': {
            professional: '切入传球',
            playerSlang: '直塞'
        },
        'Jockey': {
            professional: '跟防',
            playerSlang: '螃蟹步'
        },
        'Block': {
            professional: '封堵',
            playerSlang: '封堵'
        },
        'Intercept': {
            professional: '拦截',
            playerSlang: '钩子'
        },
        'Anticipate': {
            professional: '预判',
            playerSlang: '狐狸'
        },
        'Slide Tackle': {
            professional: '滑铲',
            playerSlang: '滑铲'
        },
        'Aerial Fortress': {
            professional: '空中堡垒',
            playerSlang: '空中堡垒'
        },
        'Technical': {
            professional: '技术',
            playerSlang: '游龙'
        },
        'Rapid': {
            professional: '灵动迅捷',
            playerSlang: '跑跑'
        },
        'First Touch': {
            professional: '第一脚触球',
            playerSlang: '磁铁'
        },
        'Trickster': {
            professional: '诡术师',
            playerSlang: '魔术师'
        },
        'Press Proven': {
            professional: '紧逼好手',
            playerSlang: '紧逼好手'
        },
        'Quick Step': {
            professional: '健步如飞',
            playerSlang: '火箭'
        },
        'Relentless': {
            professional: '坚持不懈',
            playerSlang: '电池'
        },
        'Long Throw': {
            professional: '远距离界外球',
            playerSlang: '手抛球'
        },
        'Bruiser': {
            professional: '斗士',
            playerSlang: '肌肉'
        },
        'Enforcer': {
            professional: '执行者',
            playerSlang: '护球盘带'
        },
        'Far Throw': {
            professional: '远距离抛球',
            playerSlang: '门将抛球'
        },
        'Footwork': {
            professional: '步法',
            playerSlang: '用脚扑救'
        },
        'Cross Claimer': {
            professional: '传中没收者',
            playerSlang: '传中拦截'
        },
        'Rush Out': {
            professional: '1对1紧逼',
            playerSlang: '出击'
        },
        'Far Reach': {
            professional: '远距离出击',
            playerSlang: '拦远射'
        },
        'Deflector': {
            professional: '偏转',
            playerSlang: '击球出界'
        }
    };

    // 根据开关选择当前使用的翻译词典
    const translations = {};
    for (const [english, translations_obj] of Object.entries(translationDict)) {
        translations[english] = usePlayerSlang ? translations_obj.playerSlang : translations_obj.professional;
    }
    // ==================== 翻译词典配置结束 ====================

    const sixStatSheetUrl = 'https://docs.google.com/spreadsheets/d/11MELa9ps6Eulr-82bw_gXP5owuV4J6HYrGBOlxH44_E/export?format=csv&gid=0';

    let sixStatMap = null;
    let sixStatLoaded = false;
    let sixStatLoading = false;

    // 已处理的元素集合,避免重复处理
    const processedElements = new WeakSet();

    // 安全转义用于正则的特殊字符
    function escapeRegExp(str) {
        return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
    }

    function loadSixStatTranslations() {
        if (sixStatLoaded || sixStatLoading) {
            return;
        }
        if (location.hostname.indexOf('futbin.com') === -1) {
            sixStatLoaded = true;
            sixStatMap = {};
            return;
        }
        sixStatLoading = true;
        fetch(sixStatSheetUrl)
            .then(response => {
                if (!response.ok) {
                    throw new Error('Network response was not ok');
                }
                return response.text();
            })
            .then(text => {
                const map = {};
                const lines = text.split(/\r?\n/);
                for (let i = 1; i < lines.length; i++) {
                    const line = lines[i];
                    if (!line) {
                        continue;
                    }
                    const parts = line.split(',');
                    if (parts.length < 2) {
                        continue;
                    }
                    const english = (parts[0] || '').trim();
                    const official = (parts[1] || '').trim();
                    const slang = (parts[2] || '').trim();
                    if (!english) {
                        continue;
                    }
                    let chinese = '';
                    if (usePlayerSlang) {
                        chinese = slang || official;
                    } else {
                        chinese = official || slang;
                    }
                    if (!chinese) {
                        continue;
                    }
                    map[english] = chinese;
                }
                sixStatMap = map;
                sixStatLoaded = true;
                if (Object.keys(sixStatMap).length > 0) {
                    applySixStatTranslations(document);
                }
            })
            .catch(() => {
                sixStatLoaded = true;
            });
    }

    function applySixStatTranslations(root) {
        if (!sixStatLoaded || !sixStatMap || !Object.keys(sixStatMap).length) {
            return;
        }
        if (!root) {
            root = document;
        }
        if (location.hostname.indexOf('futbin.com') === -1) {
            return;
        }
        const elements = root.querySelectorAll('div.player-stat-name.text-ellipsis');
        elements.forEach(el => {
            if (!el || el.dataset.sixStatTranslated === '1') {
                return;
            }
            const text = (el.textContent || '').trim();
            if (!text) {
                return;
            }
            const chinese = sixStatMap[text];
            if (!chinese) {
                return;
            }
            el.textContent = `${chinese} (${text})`;
            el.dataset.sixStatTranslated = '1';
        });
    }

    function applyEaMatchStyleDeltaTranslations(root) {
        if (location.hostname.indexOf('ea.com') === -1) {
            return;
        }
        if (!root) {
            root = document;
        }
        const containers = root.querySelectorAll('.ut-academy-slot-stat-view');
        containers.forEach(container => {
            const titleEl = container.querySelector('.ut-academy-slot-stat-view--title');
            if (!titleEl) {
                return;
            }
            const titleText = (titleEl.textContent || '').trim();
            if (titleText !== '比赛风格') {
                return;
            }
            const deltaEl = container.querySelector('.ut-academy-slot-stat-view--delta');
            if (!deltaEl || deltaEl.dataset.matchStyleTranslated === '1') {
                return;
            }
            let text = (deltaEl.textContent || '').trim();
            if (!text) {
                return;
            }
            if (!usePlayerSlang) {
                return;
            }
            let newText = text;
            let hasChanges = false;
            for (const [, translations_obj] of Object.entries(translationDict)) {
                const officialTranslation = translations_obj.professional;
                const slangTranslation = translations_obj.playerSlang;
                if (!officialTranslation || !slangTranslation || officialTranslation === slangTranslation) {
                    continue;
                }
                if (newText.includes(`(${officialTranslation})`)) {
                    continue;
                }
                const officialRegex = new RegExp(escapeRegExp(officialTranslation), 'g');
                if (officialRegex.test(newText)) {
                    newText = newText.replace(officialRegex, `${slangTranslation}(${officialTranslation})`);
                    hasChanges = true;
                }
            }
            if (hasChanges) {
                deltaEl.textContent = newText;
                deltaEl.dataset.matchStyleTranslated = '1';
            }
        });
    }

    loadSixStatTranslations();

    // 翻译文本节点的函数
    function translateTextNode(textNode) {
        const text = textNode.textContent;
        let newText = text;
        let hasChanges = false;

        // 遍历所有翻译词条
        for (const [english, translations_obj] of Object.entries(translationDict)) {
            const chinese = usePlayerSlang ? translations_obj.playerSlang : translations_obj.professional;

            // 使用正则表达式匹配单词边界,确保大小写敏感
            // \b 表示单词边界,确保只匹配完整的单词
            // 注意:只匹配首字母大写的版本
            const escapedEnglish = escapeRegExp(english);
            const regex = new RegExp(`\\b${escapedEnglish}\\b`, 'g');

            // 检查是否包含该单词且未被翻译过
            if (regex.test(text) && !text.includes(`(${english})`)) {
                // 替换为:中文翻译 + (英文单词)
                newText = newText.replace(regex, `${chinese} (${english})`);
                hasChanges = true;
            }

            // 当开关为【民间叫法】时,检查并替换官方翻译版本
            // 在 ea.com Web App 上不走这里,改由专门的比赛风格逻辑处理
            if (usePlayerSlang && translations_obj.professional !== translations_obj.playerSlang && location.hostname.indexOf('ea.com') === -1) {
                const officialTranslation = translations_obj.professional;
                // 中文不存在单词边界,直接做精确字符串替换
                const officialRegex = new RegExp(escapeRegExp(officialTranslation), 'g');

                // 检查是否包含官方翻译且未被处理过
                if (officialRegex.test(text) && !text.includes(`(${officialTranslation})`)) {
                    // 替换为:民间叫法 + (官方翻译);无空格,示例:火箭(健步如飞)
                    newText = newText.replace(officialRegex, `${translations_obj.playerSlang}(${officialTranslation})`);
                    hasChanges = true;
                }
            }
        }

        // 如果有变化,更新文本节点
        if (hasChanges) {
            textNode.textContent = newText;
        }
    }

    // 处理单个元素及其子节点
    function processElement(element) {
        // 跳过已处理的元素
        if (processedElements.has(element)) {
            return;
        }

        // 跳过 script 和 style 标签
        if (element.tagName === 'SCRIPT' || element.tagName === 'STYLE') {
            return;
        }

        // 遍历所有子节点
        const walker = document.createTreeWalker(
            element,
            NodeFilter.SHOW_TEXT,
            {
                acceptNode: function(node) {
                    // 跳过空文本节点
                    if (node.textContent.trim().length === 0) {
                        return NodeFilter.FILTER_REJECT;
                    }
                    // 跳过已处理的节点
                    if (processedElements.has(node)) {
                        return NodeFilter.FILTER_REJECT;
                    }
                    return NodeFilter.FILTER_ACCEPT;
                }
            }
        );

        const textNodes = [];
        let node;
        while (node = walker.nextNode()) {
            textNodes.push(node);
        }

        // 处理所有文本节点
        textNodes.forEach(textNode => {
            translateTextNode(textNode);
            processedElements.add(textNode);
        });

        processedElements.add(element);
    }

    // 处理整个页面
    function translatePage() {
        // 处理 body 元素
        processElement(document.body);
        applyEaMatchStyleDeltaTranslations(document);
    }

    // 初始加载时翻译
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', translatePage);
    } else {
        translatePage();
    }

    // 使用 MutationObserver 监听 DOM 变化,处理动态加载的内容
    const observer = new MutationObserver((mutations) => {
        mutations.forEach((mutation) => {
            mutation.addedNodes.forEach((node) => {
                // 只处理元素节点
                if (node.nodeType === Node.ELEMENT_NODE) {
                    processElement(node);
                    applySixStatTranslations(node);
                    applyEaMatchStyleDeltaTranslations(node);
                }
            });
        });
    });

    // 开始观察
    observer.observe(document.body, {
        childList: true,
        subtree: true
    });

    console.log(`翻译脚本已加载 - 当前模式: ${usePlayerSlang ? '玩家常用说法' : '官方翻译'}`);
})();