Greasy Fork is available in English.
在常用的FC UT攻略网站 fut.gg、futbin.com 和 easysbc.io 上自动为指定名词添加中文翻译
当前为
// ==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 ? '玩家常用说法' : '官方翻译'}`);
})();