// ==UserScript==
// @name OAO-FC26 UT模式翻译插件
// @namespace https://github.com/
// @version 1.6.0
// @description 中文翻译Futbin、Futgg网站的英文信息,包括球员信息、进化名称等
// @author 我要欧啊欧
// @match https://www.fut.gg/*
// @match https://fut.gg/*
// @match https://www.futbin.com/*
// @match https://futbin.com/*
// @match https://www.easysbc.io/*
// @match https://easysbc.io/*
// @connect gitee.com
// @connect github.com
// @connect cdn.jsdelivr.net
// @connect rawcdn.githack.com
// @connect giteeusercontent.com
// @connect raw.githubusercontent.com
// @connect example.com
// @connect open.feishu.cn
// @connect scriptcat.org
// @grant GM_xmlhttpRequest
// @grant GM_setValue
// @grant GM_getValue
// @license MIT
// ==/UserScript==
(() => {
'use strict';
// console.log('[OAO] 脚本开始执行');
// console.log('[OAO] 当前页面:', window.location.href);
// console.log('[OAO] 当前域名:', window.location.hostname);
// 存储相关函数
function getStorageValue(key) {
try {
const value = GM_getValue(key, null);
return value ? JSON.parse(value) : null;
} catch (error) {
console.warn('Failed to get storage value:', error);
return null;
}
}
// 设置缓存
function setStorageValue(key, value) {
try {
GM_setValue(key, JSON.stringify(value));
} catch (error) {
console.warn('Failed to set storage value:', error);
}
}
const _d = ['www.fut.gg', 'fut.gg', 'www.futbin.com', 'futbin.com', 'www.easysbc.io', 'easysbc.io'];
const _h = window.location.hostname;
// console.log('[OAO] 域名检查:', { hostname: _h, allowedDomains: _d, isAllowed: _d.includes(_h) });
if (!_d.includes(_h)) {
// console.log('[OAO] 域名不在允许列表中,脚本停止执行');
return;
}
// console.log('[OAO] 域名检查通过,继续执行');
const _s = 'OAO-FC26-v1.5.0-20260325';
const _c = getStorageValue('oaevo_code_signature');
if (!_c || _c !== _s) {
setStorageValue('oaevo_code_signature', _s);
}
const _k = 'OAO-FC26-2026';
function _e(data) {
const _t = JSON.stringify(data);
const _x = [];
for (let i = 0; i < _t.length; i++) {
_x.push(_t.charCodeAt(i) ^ _k.charCodeAt(i % _k.length));
}
return btoa(String.fromCharCode(..._x));
}
function _d0(encryptedStr) {
try {
const _y = atob(encryptedStr);
const _z = [];
for (let i = 0; i < _y.length; i++) {
_z.push(_y.charCodeAt(i) ^ _k.charCodeAt(i % _k.length));
}
return JSON.parse(String.fromCharCode(..._z));
} catch (error) {
return null;
}
}
// ========== 配置 ==========
const USE_SHORTEN_TERMS = true;
const SHOW_ORIGINAL_WITH_BRACKETS = false;
const LOCATION_CHECK_INTERVAL_MS = 10000; // 从3秒改为10秒,降低检查频率
const EVOLUTIONS_STORAGE_KEY = 'oaevo_evolutions_dict_cache';
const THREE_HOURS_MS = 5 * 60 * 1000; // 3小时
// 多数据源配置(优先使用 Gitee,国内访问更快)
const EVOLUTIONS_DICT_URLS = [
'https://gitee.com/rickma11/OAO-evotrans/raw/master/evolutions.json?inline=true', // 主数据源(Gitee)
'https://raw.githubusercontent.com/rickma11/OAO-evotrans/refs/heads/main/evolutions.json', // 备用数据源(GitHub)
'https://cdn.jsdelivr.net/gh/rickma11/OAO-evotrans@main/evolutions.json', // CDN 数据源
'https://rawcdn.githack.com/rickma11/OAO-evotrans/main/evolutions.json' // CDN 数据源
];
// 内置翻译字典多数据源配置
const BUILT_IN_DICTIONARIES_URLS = [
'https://gitee.com/rickma11/OAO-evotrans/raw/master/built-in-dictionaries.json', // 主数据源(Gitee)
'https://raw.githubusercontent.com/rickma11/OAO-evotrans/refs/heads/main/built-in-dictionaries.json', // 备用数据源(GitHub)
'https://cdn.jsdelivr.net/gh/rickma11/OAO-evotrans@main/built-in-dictionaries.json' // CDN 数据源
];
const BUILT_IN_DICTIONARIES_STORAGE_KEY = 'oaevo_built_in_dictionaries_cache';
const REMOTE_EVOLUTIONS_DICT_URL = EVOLUTIONS_DICT_URLS[0]; // 主数据源
const SIDEBAR_ID = 'oaevo-sidebar';
const SIDEBAR_DOCK_ID = 'oaevo-sidebar-dock';
const SIDEBAR_PANEL_ID = 'oaevo-sidebar-panel';
const SIDEBAR_BUTTON_CLASS = 'oaevo-sidebar-btn';
const FUTGG_TRANSLATION_ENABLED_KEY = 'oaevo_futgg_translation_enabled';
const FUTBIN_TRANSLATION_ENABLED_KEY = 'oaevo_futbin_translation_enabled';
const EASYSBC_TRANSLATION_ENABLED_KEY = 'oaevo_easysbc_translation_enabled';
const EVOLUTION_FORMAT_KEY = 'oaevo_evolution_format'; // 0: 中文(英文), 1: 中文, 2: 英文
const SIDEBAR_HIDDEN_KEY = 'oaevo_sidebar_hidden'; // 侧边栏隐藏状态
const FLOAT_ICON_POSITION_KEY = 'oaevo_float_icon_position'; // 悬浮图标位置
// 版本检查配置
const SCRIPTCAT_SCRIPT_URL = 'https://scriptcat.org/zh-CN/script-show-page/5488';
const VERSION_CHECK_KEY = 'oaevo_last_version_check_date';
const VERSION_CHECK_INTERVAL = 24 * 60 * 60 * 1000; // 24小时检查一次
const NEW_VERSION_KEY = 'oaevo_new_version_available'; // 存储新版本信息
// 更新内容配置
const UPDATE_CONTENT_URL = 'https://gitee.com/rickma11/OAO-evotrans/raw/master/catch-evo.json';
const LAST_UPDATE_CHECK_KEY = 'oaevo_last_update_check_date'; // 最后检查更新内容的日期
const UPDATE_CONTENT_KEY = 'oaevo_update_content_cache'; // 缓存更新内容
// 全局变量存储新版本信息
let newVersionInfo = null;
// 自定义翻译配置
const CUSTOM_TRANSLATIONS_KEY = 'oaevo_custom_translations';
// 标记翻译管理弹窗是否有修改(使用window对象确保全局可访问)
window.translationManagerHasChanges = false;
// 初始化翻译开关状态
let futggTranslationEnabled = getStorageValue(FUTGG_TRANSLATION_ENABLED_KEY) !== false; // 默认打开
let futbinTranslationEnabled = getStorageValue(FUTBIN_TRANSLATION_ENABLED_KEY) !== false; // 默认打开
let easysbcTranslationEnabled = getStorageValue(EASYSBC_TRANSLATION_ENABLED_KEY) !== false; // 默认打开
let evolutionFormat = getStorageValue(EVOLUTION_FORMAT_KEY) || 0; // 默认0: 中文(英文)
// 初始化自定义翻译
let customTranslations = getStorageValue(CUSTOM_TRANSLATIONS_KEY) || {};
// 统计中文key的数量
let chineseKeyCount = 0;
for (const category in customTranslations) {
if (customTranslations.hasOwnProperty(category)) {
for (const key in customTranslations[category]) {
if (/[\u4e00-\u9fa5]/.test(key)) {
chineseKeyCount++;
}
}
}
}
// 清理可能存在的中文key的翻译
let hasChanges = false;
for (const category in customTranslations) {
if (customTranslations.hasOwnProperty(category)) {
const translations = customTranslations[category];
for (const key in translations) {
if (translations.hasOwnProperty(key)) {
// 检查key是否为中文
if (/[\u4e00-\u9fa5]/.test(key)) {
delete translations[key];
hasChanges = true;
}
}
}
// 如果分类为空,删除分类
if (Object.keys(translations).length === 0) {
delete customTranslations[category];
hasChanges = true;
}
}
}
// 保存清理后的customTranslations(无论是否为空都要保存)
if (hasChanges) {
setStorageValue(CUSTOM_TRANSLATIONS_KEY, customTranslations);
}
// 侧边栏样式
const SIDEBAR_STYLES = `
#${SIDEBAR_ID}{
position:fixed;
top:140px;
right:0;
z-index:99998;
display:flex;
flex-direction:column;
align-items:center;
transform:translateX(0);
transition:transform .18s ease,opacity .12s ease;
font-family:inherit;
}
#${SIDEBAR_ID}.expanded{
transform:translateX(0);
}
#${SIDEBAR_ID}.dragging{
transition:none;
opacity:.96;
}
#${SIDEBAR_PANEL_ID}{
background:rgba(30,30,30,.96);
border:1px solid #333;
border-radius:12px;
box-shadow:0 4px 24px #0005;
padding:10px;
display:flex;
flex-direction:column;
gap:8px;
min-width:120px;
}
#${SIDEBAR_PANEL_ID} h3{
color:#ffc800;
font-size:14px;
font-weight:700;
margin:0 0 8px 0;
text-align:center;
padding-bottom:8px;
border-bottom:1px solid #444;
}
.${SIDEBAR_BUTTON_CLASS}{
width:100px;
height:36px;
border:none;
outline:none;
cursor:pointer;
border-radius:8px;
box-shadow:0 2px 8px #0002;
font-weight:600;
transition:all .15s;
user-select:none;
font-size:12px;
}
.${SIDEBAR_BUTTON_CLASS}:hover{
filter:brightness(1.1);
}
.${SIDEBAR_BUTTON_CLASS}:active{
transform:scale(.98);
}
.oaevo-sidebar-btn--refresh{
background:#007bff;
color:#fff;
}
.${SIDEBAR_BUTTON_CLASS}--settings{
background:#4caf50;
color:#fff;
}
.${SIDEBAR_BUTTON_CLASS}--settings.disabled{
background:#999;
color:#fff;
cursor:pointer;
opacity:1;
}
.${SIDEBAR_BUTTON_CLASS}--expand{
background:#ff9800;
color:#fff;
}
.${SIDEBAR_BUTTON_CLASS}--expand.disabled{
background:#999;
color:#fff;
cursor:pointer;
opacity:1;
}
.${SIDEBAR_BUTTON_CLASS}--feature{
background:#9c27b0;
color:#fff;
}
.${SIDEBAR_BUTTON_CLASS}--evolution-format{
background:#ffc107;
color:#000;
}
.${SIDEBAR_BUTTON_CLASS}--evolution-format.chinese-only{
background:#2196f3;
color:#fff;
}
.${SIDEBAR_BUTTON_CLASS}--evolution-format.english-only{
background:#f44336;
color:#fff;
}
.oaevo-sidebar-label{
display:flex;
flex-direction:column;
justify-content:center;
align-items:center;
font-size:10px;
color:#aaa;
margin-top:4px;
}
`;
// 注入侧边栏样式
function injectSidebarStyles() {
const style = document.createElement('style');
style.textContent = SIDEBAR_STYLES;
document.head.appendChild(style);
}
// 移除侧边栏
function removeSidebar() {
const sidebar = document.getElementById(SIDEBAR_ID);
if (sidebar) {
sidebar.remove();
}
}
// 同步侧边栏显示状态
function syncSidebarState() {
console.log('[OAO] syncSidebarState 开始执行');
const isFutggPage = window.location.hostname.includes('fut.gg');
const isEasysbcPage = window.location.hostname.includes('easysbc.io');
const isFutbinPage = window.location.hostname.includes('futbin.com');
console.log('[OAO] syncSidebarState 页面类型:', { isFutggPage, isEasysbcPage, isFutbinPage });
console.log('Syncing sidebar state:', {
hostname: window.location.hostname,
pathname: window.location.pathname,
isFutggPage,
isEasysbcPage,
isFutbinPage,
bodyExists: !!document.body,
readyState: document.readyState
});
// 确保 body 存在且页面加载完成
if (!document.body || document.readyState === 'loading') {
console.log('Page not ready, delaying sidebar creation...');
setTimeout(() => syncSidebarState(), 100);
return;
}
if (isFutggPage || isFutbinPage || isEasysbcPage) {
console.log('Creating sidebar for FUTGG/FUTBIN/EASYSBC page');
createSidebar();
// 检查是否应该保持隐藏状态
const isHidden = getStorageValue(SIDEBAR_HIDDEN_KEY) === true;
if (isHidden) {
const panel = document.getElementById(SIDEBAR_PANEL_ID);
if (panel) {
panel.style.display = 'none';
createFloatIcon();
}
}
} else {
console.log('Removing sidebar for non-FUTGG/FUTBIN/EASYSBC page');
removeSidebar();
}
console.log('[OAO] syncSidebarState 执行完成');
}
// 创建可拖动的悬浮图标
function createFloatIcon() {
// 如果已存在,不重复创建
if (document.getElementById('oaevo-float-icon')) {
return;
}
const floatIcon = document.createElement('div');
floatIcon.id = 'oaevo-float-icon';
floatIcon.style.position = 'fixed';
floatIcon.style.width = '50px';
floatIcon.style.height = '50px';
floatIcon.style.borderRadius = '50%';
floatIcon.style.backgroundColor = '#fff';
floatIcon.style.boxShadow = '0 2px 10px rgba(0,0,0,0.2)';
floatIcon.style.cursor = 'grab';
floatIcon.style.zIndex = '9999';
floatIcon.style.display = 'flex';
floatIcon.style.alignItems = 'center';
floatIcon.style.justifyContent = 'center';
floatIcon.style.transition = 'box-shadow 0.3s ease';
floatIcon.style.userSelect = 'none';
// 恢复保存的位置,默认在右侧中间
const savedPosition = getStorageValue(FLOAT_ICON_POSITION_KEY);
if (savedPosition) {
floatIcon.style.right = savedPosition.right + 'px';
floatIcon.style.top = savedPosition.top + 'px';
floatIcon.style.transform = 'none';
} else {
floatIcon.style.right = '10px';
floatIcon.style.top = '50%';
floatIcon.style.transform = 'translateY(-50%)';
}
// 添加图标
const icon = document.createElement('img');
icon.src = 'https://s41.ax1x.com/2026/03/01/peS2bPP.png';
icon.style.width = '50px';
icon.style.height = '50px';
icon.style.objectFit = 'contain';
icon.style.pointerEvents = 'none'; // 防止图标干扰拖动
icon.onerror = function() {
floatIcon.innerHTML = 'OAO';
floatIcon.style.fontSize = '16px';
floatIcon.style.fontWeight = 'bold';
floatIcon.style.color = '#000';
};
floatIcon.appendChild(icon);
// 拖动相关变量
let isDragging = false;
let startX, startY, startRight, startTop;
// 开始拖动的通用函数
function startDrag(e) {
isDragging = false; // 重置拖动状态
const clientX = e.clientX || e.touches[0].clientX;
const clientY = e.clientY || e.touches[0].clientY;
startX = clientX;
startY = clientY;
const rect = floatIcon.getBoundingClientRect();
startRight = window.innerWidth - rect.right;
startTop = rect.top;
floatIcon.style.cursor = 'grabbing';
floatIcon.style.transform = 'none';
e.preventDefault();
}
// 移动的通用函数
function moveDrag(e) {
if (startX === undefined) return;
const clientX = e.clientX || e.touches[0].clientX;
const clientY = e.clientY || e.touches[0].clientY;
const deltaX = startX - clientX;
const deltaY = clientY - startY;
// 移动超过5px才算拖动
if (Math.abs(deltaX) > 5 || Math.abs(deltaY) > 5) {
isDragging = true;
}
if (isDragging) {
let newRight = startRight + deltaX;
let newTop = startTop + deltaY;
// 限制在屏幕范围内
newRight = Math.max(0, Math.min(newRight, window.innerWidth - 50));
newTop = Math.max(0, Math.min(newTop, window.innerHeight - 50));
floatIcon.style.right = newRight + 'px';
floatIcon.style.top = newTop + 'px';
}
}
// 结束拖动的通用函数
function endDrag() {
if (startX !== undefined) {
floatIcon.style.cursor = 'grab';
// 保存位置
const rect = floatIcon.getBoundingClientRect();
setStorageValue(FLOAT_ICON_POSITION_KEY, {
right: window.innerWidth - rect.right,
top: rect.top
});
// 检测是否是点击(非拖动)
if (!isDragging) {
const panel = document.getElementById(SIDEBAR_PANEL_ID);
if (panel) {
panel.style.display = 'flex';
setStorageValue(SIDEBAR_HIDDEN_KEY, false);
}
floatIcon.remove();
}
startX = undefined;
startY = undefined;
}
}
// 鼠标事件
floatIcon.addEventListener('mousedown', startDrag);
document.addEventListener('mousemove', moveDrag);
document.addEventListener('mouseup', endDrag);
// 触摸事件(支持手机端)
floatIcon.addEventListener('touchstart', startDrag, { passive: false });
document.addEventListener('touchmove', moveDrag, { passive: false });
document.addEventListener('touchend', endDrag);
// 点击显示侧边栏(非拖动时)
floatIcon.addEventListener('click', () => {
if (!isDragging) {
const panel = document.getElementById(SIDEBAR_PANEL_ID);
if (panel) {
panel.style.display = 'flex';
setStorageValue(SIDEBAR_HIDDEN_KEY, false);
}
floatIcon.remove();
}
});
// 悬浮效果
floatIcon.addEventListener('mouseenter', () => {
if (!isDragging) {
floatIcon.style.boxShadow = '0 4px 15px rgba(0,0,0,0.3)';
}
});
floatIcon.addEventListener('mouseleave', () => {
floatIcon.style.boxShadow = '0 2px 10px rgba(0,0,0,0.2)';
});
// 确保 body 存在
if (!document.body) {
setTimeout(() => createFloatIcon(), 100);
return;
}
document.body.appendChild(floatIcon);
}
// 创建侧边栏
function createSidebar() {
console.log('[OAO] createSidebar 开始执行');
if (document.getElementById(SIDEBAR_ID)) {
console.log('[OAO] 侧边栏已存在,跳过创建');
return;
}
console.log('[OAO] 开始创建侧边栏');
const sidebar = document.createElement('div');
sidebar.id = SIDEBAR_ID;
const panel = document.createElement('div');
panel.id = SIDEBAR_PANEL_ID;
const title = document.createElement('h3');
title.textContent = 'OAO翻译插件';
const refreshBtn = document.createElement('button');
refreshBtn.className = `${SIDEBAR_BUTTON_CLASS} ${SIDEBAR_BUTTON_CLASS}--refresh`;
refreshBtn.textContent = '更新翻译';
refreshBtn.addEventListener('click', handleRefreshButtonClick);
const isFutggPage = window.location.hostname.includes('fut.gg');
const isEasysbcPage = window.location.hostname.includes('easysbc.io');
const isFutbinPage = window.location.hostname.includes('futbin.com');
console.log('[OAO] createSidebar 页面类型:', { isFutggPage, isEasysbcPage, isFutbinPage });
console.log('[OAO] 当前hostname:', window.location.hostname);
console.log('[OAO] easysbc判断结果:', window.location.hostname.includes('easysbc.io'));
const settingsBtn = document.createElement('button');
settingsBtn.className = `${SIDEBAR_BUTTON_CLASS} ${SIDEBAR_BUTTON_CLASS}--settings`;
// 优先检查EASYSBC页面
if (isEasysbcPage) {
console.log('[OAO] 创建EASYSBC翻译按钮');
settingsBtn.textContent = easysbcTranslationEnabled ? 'EASYSBC翻译: 开' : 'EASYSBC翻译: 关';
if (!easysbcTranslationEnabled) {
settingsBtn.classList.add('disabled');
}
settingsBtn.addEventListener('click', () => {
// 切换翻译开关状态
easysbcTranslationEnabled = !easysbcTranslationEnabled;
// 保存状态到本地存储
setStorageValue(EASYSBC_TRANSLATION_ENABLED_KEY, easysbcTranslationEnabled);
// 更新按钮文本和样式
settingsBtn.textContent = easysbcTranslationEnabled ? 'EASYSBC翻译: 开' : 'EASYSBC翻译: 关';
if (easysbcTranslationEnabled) {
settingsBtn.classList.remove('disabled');
} else {
settingsBtn.classList.add('disabled');
}
// 立即重新翻译页面内容
if (window.location.hostname.includes('easysbc.io')) {
if (easysbcTranslationEnabled) {
// 如果打开翻译,重新翻译整个页面
translateRoot(document);
} else {
// 如果关闭翻译,刷新页面恢复原始内容
location.reload();
}
}
});
} else if (isFutggPage) {
// console.log('[OAO] 创建FUTGG翻译按钮');
settingsBtn.textContent = futggTranslationEnabled ? 'FUTGG翻译: 开' : 'FUTGG翻译: 关';
if (!futggTranslationEnabled) {
settingsBtn.classList.add('disabled');
}
settingsBtn.addEventListener('click', () => {
// 切换翻译开关状态
futggTranslationEnabled = !futggTranslationEnabled;
// 保存状态到本地存储
setStorageValue(FUTGG_TRANSLATION_ENABLED_KEY, futggTranslationEnabled);
// 更新按钮文本和样式
settingsBtn.textContent = futggTranslationEnabled ? 'FUTGG翻译: 开' : 'FUTGG翻译: 关';
if (futggTranslationEnabled) {
settingsBtn.classList.remove('disabled');
} else {
settingsBtn.classList.add('disabled');
}
// 立即重新翻译页面内容
if (window.location.hostname.includes('fut.gg')) {
if (futggTranslationEnabled) {
// 如果打开翻译,重新翻译整个页面
translateRoot(document);
} else {
// 如果关闭翻译,刷新页面恢复原始内容
location.reload();
}
}
});
} else {
// console.log('[OAO] 创建FUTBIN翻译按钮');
settingsBtn.textContent = futbinTranslationEnabled ? 'FUTBIN翻译: 开' : 'FUTBIN翻译: 关';
if (!futbinTranslationEnabled) {
settingsBtn.classList.add('disabled');
}
settingsBtn.addEventListener('click', () => {
// 切换翻译开关状态
futbinTranslationEnabled = !futbinTranslationEnabled;
// 保存状态到本地存储
setStorageValue(FUTBIN_TRANSLATION_ENABLED_KEY, futbinTranslationEnabled);
// 更新按钮文本和样式
settingsBtn.textContent = futbinTranslationEnabled ? 'FUTBIN翻译: 开' : 'FUTBIN翻译: 关';
if (futbinTranslationEnabled) {
settingsBtn.classList.remove('disabled');
} else {
settingsBtn.classList.add('disabled');
}
// 立即重新翻译页面内容
if (!window.location.hostname.includes('fut.gg') && !window.location.hostname.includes('easysbc.io')) {
if (futbinTranslationEnabled) {
// 如果打开翻译,重新翻译整个页面
translateRoot(document);
} else {
// 如果关闭翻译,刷新页面恢复原始内容
location.reload();
}
}
});
}
// 只在FUTBIN页面创建FUTBIN翻译按钮
let expandBtn;
if (!isEasysbcPage && !isFutggPage) {
expandBtn = document.createElement('button');
expandBtn.className = `${SIDEBAR_BUTTON_CLASS} ${SIDEBAR_BUTTON_CLASS}--expand`;
expandBtn.textContent = futbinTranslationEnabled ? 'FUTBIN翻译: 开' : 'FUTBIN翻译: 关';
if (!futbinTranslationEnabled) {
expandBtn.classList.add('disabled');
}
expandBtn.addEventListener('click', () => {
// 切换翻译开关状态
futbinTranslationEnabled = !futbinTranslationEnabled;
// 保存状态到本地存储
setStorageValue(FUTBIN_TRANSLATION_ENABLED_KEY, futbinTranslationEnabled);
// 更新按钮文本和样式
expandBtn.textContent = futbinTranslationEnabled ? 'FUTBIN翻译: 开' : 'FUTBIN翻译: 关';
if (futbinTranslationEnabled) {
expandBtn.classList.remove('disabled');
} else {
expandBtn.classList.add('disabled');
}
// 立即重新翻译页面内容
if (!window.location.hostname.includes('fut.gg') && !window.location.hostname.includes('easysbc.io')) {
if (futbinTranslationEnabled) {
// 如果打开翻译,重新翻译整个页面
translateRoot(document);
} else {
// 如果关闭翻译,刷新页面恢复原始内容
location.reload();
}
}
});
// 移除这里的panel.appendChild(expandBtn),在下面统一处理按钮顺序
}
// 在EASYSBC页面创建EASYSBC翻译按钮
let easysbcBtn;
if (isEasysbcPage) {
easysbcBtn = document.createElement('button');
easysbcBtn.className = `${SIDEBAR_BUTTON_CLASS} ${SIDEBAR_BUTTON_CLASS}--expand`;
easysbcBtn.textContent = easysbcTranslationEnabled ? 'EASY翻译: 开' : 'EASY翻译: 关';
if (!easysbcTranslationEnabled) {
easysbcBtn.classList.add('disabled');
}
easysbcBtn.addEventListener('click', () => {
// 切换翻译开关状态
easysbcTranslationEnabled = !easysbcTranslationEnabled;
// 保存状态到本地存储
setStorageValue(EASYSBC_TRANSLATION_ENABLED_KEY, easysbcTranslationEnabled);
// 更新按钮文本和样式
easysbcBtn.textContent = easysbcTranslationEnabled ? 'EASY翻译: 开' : 'EASY翻译: 关';
if (easysbcTranslationEnabled) {
easysbcBtn.classList.remove('disabled');
} else {
easysbcBtn.classList.add('disabled');
}
// 立即重新翻译页面内容
if (window.location.hostname.includes('easysbc.io')) {
if (easysbcTranslationEnabled) {
// 如果打开翻译,重新翻译整个页面
translateRoot(document);
} else {
// 如果关闭翻译,刷新页面恢复原始内容
location.reload();
}
}
});
// 移除这里的panel.appendChild(easysbcBtn),在下面统一处理按钮顺序
}
const featureBtn = document.createElement('button');
featureBtn.className = `${SIDEBAR_BUTTON_CLASS} ${SIDEBAR_BUTTON_CLASS}--feature`;
featureBtn.textContent = '便捷入口';
featureBtn.addEventListener('click', () => {
// 创建居中弹窗
const modal = document.createElement('div');
modal.style.position = 'fixed';
modal.style.top = '0';
modal.style.left = '0';
modal.style.width = '100%';
modal.style.height = '100%';
modal.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
modal.style.display = 'flex';
modal.style.alignItems = 'center';
modal.style.justifyContent = 'center';
modal.style.zIndex = '99999';
modal.style.backdropFilter = 'blur(5px)';
// 弹窗内容
const modalContent = document.createElement('div');
modalContent.style.backgroundColor = '#fff';
modalContent.style.borderRadius = '12px';
modalContent.style.padding = '30px';
modalContent.style.width = '400px';
modalContent.style.maxWidth = '90%';
modalContent.style.boxShadow = '0 10px 40px rgba(0, 0, 0, 0.3)';
// 标题
const title = document.createElement('h3');
title.textContent = '快捷入口';
title.style.color = '#333';
title.style.fontSize = '25px';
title.style.fontWeight = '700';
title.style.margin = '0 0 20px 0';
title.style.textAlign = 'center';
// 按钮容器
const buttonContainer = document.createElement('div');
buttonContainer.style.display = 'flex';
buttonContainer.style.flexDirection = 'column';
buttonContainer.style.gap = '12px';
// 快捷入口按钮
const quickLinks = [
{ text: '进入 UT WEB', url: 'https://www.ea.com/ea-sports-fc/ultimate-team/web-app/' },
{ text: '进入 FUTBIN', url: 'https://www.futbin.com/' },
{ text: '进入 FUTGG', url: 'https://www.fut.gg/' },
{ text: '进入 EASYSBC', url: 'https://www.easysbc.io/' },
{ text: '进入 FUTGG-进化实验室', url: 'https://www.fut.gg/evo-lab/' }
];
quickLinks.forEach(link => {
const btn = document.createElement('button');
btn.textContent = link.text;
btn.style.width = '100%';
btn.style.height = '40px';
btn.style.border = 'none';
btn.style.borderRadius = '8px';
btn.style.backgroundColor = '#027ba0ff';
btn.style.color = '#fff';
btn.style.fontWeight = '600';
btn.style.fontSize = '14px';
btn.style.cursor = 'pointer';
btn.style.transition = 'all 0.2s ease';
btn.addEventListener('mouseenter', () => {
btn.style.backgroundColor = '#015a7a';
btn.style.transform = 'translateY(-2px)';
});
btn.addEventListener('mouseleave', () => {
btn.style.backgroundColor = '#027ba0ff';
btn.style.transform = 'translateY(0)';
});
btn.addEventListener('click', () => {
window.open(link.url, '_blank');
});
buttonContainer.appendChild(btn);
});
// 特别推荐部分
const specialTitle = document.createElement('h4');
specialTitle.textContent = '特别推荐';
specialTitle.style.color = '#333';
specialTitle.style.fontSize = '18px';
specialTitle.style.fontWeight = '700';
specialTitle.style.margin = '20px 0 10px 0';
specialTitle.style.textAlign = 'center';
const specialContainer = document.createElement('div');
specialContainer.style.display = 'flex';
specialContainer.style.flexDirection = 'column';
specialContainer.style.gap = '8px';
const specialLinks = [
{ text: '每日开包视频', url: 'https://space.bilibili.com/21399808/dynamic' },
{ text: 'SBC球员评测', url: 'https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzA5MzE2NzgyMQ==&action=getalbum&album_id=3721449259213029385#wechat_redirect' },
{ text: '每周阵型战术推荐', url: 'https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzA5MzE2NzgyMQ==&action=getalbum&album_id=3714272208722886660#wechat_redirect' }
];
specialLinks.forEach(link => {
const btn = document.createElement('button');
btn.textContent = link.text;
btn.style.width = '100%';
btn.style.height = '40px';
btn.style.border = 'none';
btn.style.borderRadius = '8px';
btn.style.backgroundColor = '#027ba0ff';
btn.style.color = '#fff';
btn.style.fontWeight = '600';
btn.style.fontSize = '14px';
btn.style.cursor = 'pointer';
btn.style.transition = 'all 0.2s ease';
btn.addEventListener('mouseenter', () => {
btn.style.backgroundColor = '#015a7a';
btn.style.transform = 'translateY(-2px)';
});
btn.addEventListener('mouseleave', () => {
btn.style.backgroundColor = '#027ba0ff';
btn.style.transform = 'translateY(0)';
});
btn.addEventListener('click', () => {
window.open(link.url, '_blank');
});
specialContainer.appendChild(btn);
});
// 术语解释部分
const termsTitle = document.createElement('h4');
termsTitle.textContent = '术语解释';
termsTitle.style.color = '#333';
termsTitle.style.fontSize = '18px';
termsTitle.style.fontWeight = '700';
termsTitle.style.margin = '20px 0 10px 0';
termsTitle.style.textAlign = 'center';
const termsContainer = document.createElement('div');
termsContainer.style.display = 'flex';
termsContainer.style.flexDirection = 'column';
termsContainer.style.gap = '8px';
const termsLinks = [
{ text: '球员角色', url: 'https://mp.weixin.qq.com/s/sdQc3Su0nSHBqTs_k5nmiw' },
{ text: '球员特技', url: 'https://mp.weixin.qq.com/s/W0-JG1l533IRB7EK5HZDww' },
{ text: '化学加成&加速类型', url: 'https://mp.weixin.qq.com/s/W2o3hFm0tElcWMJ8B3zc3A' }
];
termsLinks.forEach(link => {
const btn = document.createElement('button');
btn.textContent = link.text;
btn.style.width = '100%';
btn.style.height = '40px';
btn.style.border = 'none';
btn.style.borderRadius = '8px';
btn.style.backgroundColor = '#027ba0ff';
btn.style.color = '#fff';
btn.style.fontWeight = '600';
btn.style.fontSize = '14px';
btn.style.cursor = 'pointer';
btn.style.transition = 'all 0.2s ease';
btn.addEventListener('mouseenter', () => {
btn.style.backgroundColor = '#015a7a';
btn.style.transform = 'translateY(-2px)';
});
btn.addEventListener('mouseleave', () => {
btn.style.backgroundColor = '#027ba0ff';
btn.style.transform = 'translateY(0)';
});
btn.addEventListener('click', () => {
window.open(link.url, '_blank');
});
termsContainer.appendChild(btn);
});
// 其他功能部分
const otherTitle = document.createElement('h4');
otherTitle.textContent = '其他功能';
otherTitle.style.color = '#333';
otherTitle.style.fontSize = '18px';
otherTitle.style.fontWeight = '700';
otherTitle.style.margin = '20px 0 10px 0';
otherTitle.style.textAlign = 'center';
const otherContainer = document.createElement('div');
otherContainer.style.display = 'flex';
otherContainer.style.flexDirection = 'column';
otherContainer.style.gap = '8px';
otherContainer.className = 'other-container';
// 注释掉:检查是否有今日更新
// console.log('=== 检查今日更新 ===');
// const cachedData = getStorageValue(UPDATE_CONTENT_KEY);
// console.log('缓存的更新内容:', cachedData);
// let hasTodayUpdate = false;
// let updateData = null;
// if (cachedData) {
// try {
// updateData = JSON.parse(cachedData);
// console.log('解析更新内容:', updateData);
// if (updateData && updateData.date) {
// const today = new Date().toISOString().split('T')[0];
// const updateDate = updateData.date;
// console.log('今日日期:', today);
// console.log('更新日期:', updateDate);
// hasTodayUpdate = (updateDate === today || updateDate.includes(today));
// console.log('是否有今日更新:', hasTodayUpdate);
// }
// } catch (error) {
// console.error('解析缓存更新内容失败:', error);
// }
// }
// 注释掉:如果有今日更新,添加今日更新按钮
// if (hasTodayUpdate && updateData) {
// console.log('添加今日更新按钮');
// const updateBtn = document.createElement('button');
// updateBtn.textContent = '今日更新';
// updateBtn.style.cssText = `
// width: 100%;
// height: 40px;
// border: none;
// border-radius: 8px;
// background: #ff6b6b;
// color: white;
// font-weight: 600;
// font-size: 14px;
// cursor: pointer;
// transition: all 0.2s ease;
// margin-bottom: 8px;
// `;
// updateBtn.addEventListener('mouseenter', () => {
// updateBtn.style.background = '#e55a5a';
// updateBtn.style.transform = 'translateY(-2px)';
// });
// updateBtn.addEventListener('mouseleave', () => {
// updateBtn.style.background = '#ff6b6b';
// updateBtn.style.transform = 'translateY(0)';
// });
// updateBtn.addEventListener('click', () => {
// console.log('点击今日更新按钮,重新拉取最新内容');
// // 重新拉取最新内容
// fetchUpdateContent((data) => {
// if (data) {
// showUpdateContentPopup(data, false);
// } else {
// // 拉取失败,显示错误提示
// showErrorPopup('信息读取失败,请稍后前往快捷入口-今日更新再次查看');
// }
// });
// });
// // 添加到其他功能区域的第一个位置
// if (otherContainer) {
// console.log('otherContainer存在,添加按钮');
// const firstChild = otherContainer.firstChild;
// if (firstChild) {
// otherContainer.insertBefore(updateBtn, firstChild);
// } else {
// otherContainer.appendChild(updateBtn);
// }
// console.log('今日更新按钮已添加');
// } else {
// console.error('otherContainer不存在');
// }
// }
const otherLinks = [
{ text: '功能介绍', url: 'https://my.feishu.cn/wiki/CfKqw17NkiTDK2kCq5tcdqI7nxd' },
{ text: '更新版本', url: 'https://scriptcat.org/zh-CN/script-show-page/5488' },
{ text: '意见反馈', url: 'https://my.feishu.cn/share/base/form/shrcnTlLN2wPLV0dzRQ33Q1kwlg' }
];
otherLinks.forEach(link => {
const btn = document.createElement('button');
btn.textContent = link.text;
btn.style.width = '100%';
btn.style.height = '40px';
btn.style.border = 'none';
btn.style.borderRadius = '8px';
btn.style.backgroundColor = '#027ba0ff';
btn.style.color = '#fff';
btn.style.fontWeight = '600';
btn.style.fontSize = '14px';
btn.style.cursor = 'pointer';
btn.style.transition = 'all 0.2s ease';
btn.addEventListener('mouseenter', () => {
btn.style.backgroundColor = '#015a7a';
btn.style.transform = 'translateY(-2px)';
});
btn.addEventListener('mouseleave', () => {
btn.style.backgroundColor = '#027ba0ff';
btn.style.transform = 'translateY(0)';
});
btn.addEventListener('click', () => {
window.open(link.url, '_blank');
});
otherContainer.appendChild(btn);
});
// 关闭按钮
const closeBtn = document.createElement('button');
closeBtn.textContent = '关闭';
closeBtn.style.width = '100%';
closeBtn.style.height = '40px';
closeBtn.style.border = '1px solid #ddd';
closeBtn.style.borderRadius = '8px';
closeBtn.style.backgroundColor = '#f5f5f5';
closeBtn.style.color = '#333';
closeBtn.style.fontWeight = '600';
closeBtn.style.fontSize = '14px';
closeBtn.style.cursor = 'pointer';
closeBtn.style.marginTop = '15px';
closeBtn.style.transition = 'all 0.2s ease';
closeBtn.addEventListener('mouseenter', () => {
closeBtn.style.backgroundColor = '#e0e0e0';
});
closeBtn.addEventListener('mouseleave', () => {
closeBtn.style.backgroundColor = '#f5f5f5';
});
closeBtn.addEventListener('click', async () => {
modal.remove();
// 只有在有修改的情况下才刷新页面
if (window.translationManagerHasChanges) {
// 清除所有元素的翻译标记,确保重新翻译
document.querySelectorAll('[data-oao-translated="true"]').forEach(element => {
delete element.dataset.oaoTranslated;
});
await translateRoot(document);
// 重置标记
window.translationManagerHasChanges = false;
}
});
// 组装弹窗
modalContent.appendChild(title);
modalContent.appendChild(buttonContainer);
modalContent.appendChild(specialTitle);
modalContent.appendChild(specialContainer);
modalContent.appendChild(termsTitle);
modalContent.appendChild(termsContainer);
modalContent.appendChild(otherTitle);
modalContent.appendChild(otherContainer);
modalContent.appendChild(closeBtn);
modal.appendChild(modalContent);
// 点击背景关闭
modal.addEventListener('click', async (e) => {
if (e.target === modal) {
modal.remove();
// 只有在有修改的情况下才刷新页面
if (window.translationManagerHasChanges) {
// 清除所有元素的翻译标记,确保重新翻译
document.querySelectorAll('[data-oao-translated="true"]').forEach(element => {
delete element.dataset.oaoTranslated;
});
await translateRoot(document);
// 重置标记
window.translationManagerHasChanges = false;
}
}
});
document.body.appendChild(modal);
});
// 打开翻译管理界面
function openTranslationManager() {
// 重置修改标记
window.translationManagerHasChanges = false;
// 创建居中弹窗
const modal = document.createElement('div');
modal.className = 'oaevo-translation-modal';
modal.style.position = 'fixed';
modal.style.top = '0';
modal.style.left = '0';
modal.style.width = '100%';
modal.style.height = '100%';
modal.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
modal.style.display = 'flex';
modal.style.alignItems = 'center';
modal.style.justifyContent = 'center';
modal.style.zIndex = '99999';
modal.style.backdropFilter = 'blur(5px)';
// 弹窗内容
const modalContent = document.createElement('div');
modalContent.style.backgroundColor = '#fff';
modalContent.style.borderRadius = '12px';
modalContent.style.padding = '30px';
modalContent.style.width = '800px';
modalContent.style.maxWidth = '95%';
modalContent.style.maxHeight = '80vh';
modalContent.style.overflow = 'auto';
modalContent.style.boxShadow = '0 10px 40px rgba(0, 0, 0, 0.3)';
// 标题容器
const titleContainer = document.createElement('div');
titleContainer.style.display = 'flex';
titleContainer.style.alignItems = 'center';
titleContainer.style.justifyContent = 'space-between';
titleContainer.style.marginBottom = '20px';
// 标题
const title = document.createElement('h3');
title.textContent = '翻译管理';
title.style.color = '#333';
title.style.fontSize = '20px';
title.style.fontWeight = '700';
title.style.margin = '0';
// 关闭按钮
const closeBtn = document.createElement('button');
closeBtn.innerHTML = '×';
closeBtn.style.width = '30px';
closeBtn.style.height = '30px';
closeBtn.style.border = 'none';
closeBtn.style.borderRadius = '50%';
closeBtn.style.backgroundColor = '#f0f0f0';
closeBtn.style.color = '#333';
closeBtn.style.fontSize = '20px';
closeBtn.style.fontWeight = 'bold';
closeBtn.style.cursor = 'pointer';
closeBtn.style.display = 'flex';
closeBtn.style.alignItems = 'center';
closeBtn.style.justifyContent = 'center';
closeBtn.style.transition = 'all 0.2s ease';
closeBtn.addEventListener('mouseenter', () => {
closeBtn.style.backgroundColor = '#e0e0e0';
});
closeBtn.addEventListener('mouseleave', () => {
closeBtn.style.backgroundColor = '#f0f0f0';
});
closeBtn.addEventListener('click', async () => {
// log('info', '关闭弹窗,translationManagerHasChanges:', window.translationManagerHasChanges);
modal.remove();
// 只有在有修改的情况下才刷新页面
if (window.translationManagerHasChanges) {
// log('info', '开始刷新页面...');
// 清除所有元素的翻译标记,确保重新翻译
document.querySelectorAll('[data-oao-translated="true"]').forEach(element => {
delete element.dataset.oaoTranslated;
});
// log('info', '清除翻译标记完成');
await translateRoot(document);
// log('info', '页面刷新完成');
// 重置标记
window.translationManagerHasChanges = false;
// log('info', '重置标记为false');
}
});
titleContainer.appendChild(title);
titleContainer.appendChild(closeBtn);
modalContent.appendChild(titleContainer);
// 说明文字
const description = document.createElement('p');
description.textContent = '中文翻译修改后需要手动刷新页面才可生效';
description.style.color = '#666';
description.style.fontSize = '13px';
description.style.margin = '0 0 20px 0';
description.style.padding = '0';
modalContent.appendChild(description);
// 搜索功能
const searchContainer = document.createElement('div');
searchContainer.style.marginBottom = '20px';
const searchInput = document.createElement('input');
searchInput.type = 'text';
searchInput.placeholder = '搜索翻译...';
searchInput.style.width = '100%';
searchInput.style.padding = '8px 12px';
searchInput.style.border = '1px solid #ccc';
searchInput.style.borderRadius = '6px';
searchInput.style.fontSize = '14px';
searchInput.style.backgroundColor = '#fff';
searchInput.style.color = '#333';
searchInput.style.boxSizing = 'border-box';
searchContainer.appendChild(searchInput);
// 分类标签
const categories = [
{ id: 'basic', name: '基础' },
{ id: 'sixStat', name: '数据' },
{ id: 'roles', name: '角色' },
{ id: 'chemistry', name: '化学' },
{ id: 'playstyles', name: '特技' },
{ id: 'futgg', name: 'FUTGG' },
{ id: 'futbin', name: 'FUTBIN' },
{ id: 'easysbc', name: 'EASYSBC' },
{ id: 'add', name: '添加' }
];
const categoryContainer = document.createElement('div');
categoryContainer.style.display = 'flex';
categoryContainer.style.gap = '10px';
categoryContainer.style.marginBottom = '20px';
categoryContainer.style.flexWrap = 'wrap';
categories.forEach(category => {
const categoryBtn = document.createElement('button');
categoryBtn.textContent = category.name;
categoryBtn.style.padding = '6px 12px';
categoryBtn.style.border = '1px solid #027ba0ff';
categoryBtn.style.borderRadius = '16px';
categoryBtn.style.backgroundColor = '#fff';
categoryBtn.style.color = '#027ba0ff';
categoryBtn.style.fontSize = '12px';
categoryBtn.style.cursor = 'pointer';
categoryBtn.style.transition = 'all 0.2s ease';
categoryBtn.addEventListener('mouseenter', () => {
// 只有在未选中状态下才改变样式
if (categoryBtn.style.backgroundColor !== 'rgb(2, 123, 160)') {
categoryBtn.style.backgroundColor = '#f0f8ff';
}
});
categoryBtn.addEventListener('mouseleave', () => {
// 只有在未选中状态下才改变样式
if (categoryBtn.style.backgroundColor !== 'rgb(2, 123, 160)') {
categoryBtn.style.backgroundColor = '#fff';
}
});
categoryBtn.addEventListener('click', () => {
// 切换分类
categories.forEach(cat => {
const btn = categoryContainer.querySelector(`[data-category="${cat.id}"]`);
if (btn) {
btn.style.backgroundColor = '#fff';
btn.style.color = '#027ba0ff';
}
});
categoryBtn.style.backgroundColor = '#027ba0ff';
categoryBtn.style.color = '#fff';
// 如果是添加标签,显示添加表单,隐藏翻译列表
if (category.id === 'add') {
translationList.style.display = 'none';
addForm.style.display = 'block';
} else {
translationList.style.display = 'block';
addForm.style.display = 'none';
// 显示对应分类的翻译
async function showCategoryTranslation() {
await ensureDictionaries();
showTranslationsByCategory(category.id);
}
showCategoryTranslation();
}
});
categoryBtn.setAttribute('data-category', category.id);
categoryContainer.appendChild(categoryBtn);
});
// 翻译列表
const translationList = document.createElement('div');
translationList.id = 'translation-list';
translationList.style.marginBottom = '20px';
// 添加翻译表单
const addForm = document.createElement('div');
addForm.style.border = '1px solid #ddd';
addForm.style.borderRadius = '8px';
addForm.style.padding = '20px';
addForm.style.marginBottom = '20px';
addForm.style.display = 'none'; // 默认隐藏
const formTitle = document.createElement('h4');
formTitle.textContent = '添加翻译';
formTitle.style.color = '#333';
formTitle.style.fontSize = '16px';
formTitle.style.fontWeight = '600';
formTitle.style.margin = '0 0 15px 0';
const formFields = document.createElement('div');
formFields.style.display = 'flex';
formFields.style.flexDirection = 'column';
formFields.style.gap = '12px';
// 原文输入
const originalContainer = document.createElement('div');
originalContainer.style.marginBottom = '12px';
const originalLabel = document.createElement('label');
originalLabel.textContent = '英语:';
originalLabel.style.display = 'block';
originalLabel.style.marginBottom = '6px';
originalLabel.style.fontSize = '14px';
originalLabel.style.fontWeight = '600';
originalLabel.style.color = '#333';
const originalInput = document.createElement('input');
originalInput.type = 'text';
originalInput.id = 'original-text';
originalInput.style.width = '100%';
originalInput.style.padding = '8px 12px';
originalInput.style.border = '1px solid #ccc';
originalInput.style.borderRadius = '6px';
originalInput.style.fontSize = '14px';
originalInput.style.backgroundColor = '#fff';
originalInput.style.color = '#333';
originalInput.style.boxSizing = 'border-box';
originalContainer.appendChild(originalLabel);
originalContainer.appendChild(originalInput);
// 翻译输入
const translationContainer = document.createElement('div');
translationContainer.style.marginBottom = '12px';
const translationLabel = document.createElement('label');
translationLabel.textContent = '中文:';
translationLabel.style.display = 'block';
translationLabel.style.marginBottom = '6px';
translationLabel.style.fontSize = '14px';
translationLabel.style.fontWeight = '600';
translationLabel.style.color = '#333';
const translationInput = document.createElement('input');
translationInput.type = 'text';
translationInput.id = 'translation-text';
translationInput.style.width = '100%';
translationInput.style.padding = '8px 12px';
translationInput.style.border = '1px solid #ccc';
translationInput.style.borderRadius = '6px';
translationInput.style.fontSize = '14px';
translationInput.style.backgroundColor = '#fff';
translationInput.style.color = '#333';
translationInput.style.boxSizing = 'border-box';
translationContainer.appendChild(translationLabel);
translationContainer.appendChild(translationInput);
// 分类选择
const categorySelectContainer = document.createElement('div');
categorySelectContainer.style.marginBottom = '12px';
const categorySelectLabel = document.createElement('label');
categorySelectLabel.textContent = '分类:';
categorySelectLabel.style.display = 'block';
categorySelectLabel.style.marginBottom = '6px';
categorySelectLabel.style.fontSize = '14px';
categorySelectLabel.style.fontWeight = '600';
categorySelectLabel.style.color = '#333';
const categorySelect = document.createElement('select');
categorySelect.id = 'category-select';
categorySelect.style.width = '100%';
categorySelect.style.padding = '8px 12px';
categorySelect.style.border = '1px solid #ccc';
categorySelect.style.borderRadius = '6px';
categorySelect.style.fontSize = '14px';
categorySelect.style.backgroundColor = '#fff';
categorySelect.style.color = '#333';
categorySelect.style.boxSizing = 'border-box';
categories.forEach(category => {
// 排除"添加"选项
if (category.id !== 'add') {
const option = document.createElement('option');
option.value = category.id;
option.textContent = category.name;
categorySelect.appendChild(option);
}
});
categorySelectContainer.appendChild(categorySelectLabel);
categorySelectContainer.appendChild(categorySelect);
formFields.appendChild(originalContainer);
formFields.appendChild(translationContainer);
formFields.appendChild(categorySelectContainer);
// 操作按钮
const formButtons = document.createElement('div');
formButtons.style.display = 'flex';
formButtons.style.gap = '10px';
formButtons.style.marginTop = '10px';
const saveBtn = document.createElement('button');
saveBtn.textContent = '保存';
saveBtn.style.flex = '1';
saveBtn.style.padding = '10px';
saveBtn.style.border = 'none';
saveBtn.style.borderRadius = '6px';
saveBtn.style.backgroundColor = '#027ba0ff';
saveBtn.style.color = '#fff';
saveBtn.style.fontWeight = '600';
saveBtn.style.fontSize = '14px';
saveBtn.style.cursor = 'pointer';
saveBtn.style.transition = 'all 0.2s ease';
saveBtn.addEventListener('mouseenter', () => {
saveBtn.style.backgroundColor = '#015a7a';
});
saveBtn.addEventListener('mouseleave', () => {
saveBtn.style.backgroundColor = '#027ba0ff';
});
saveBtn.addEventListener('click', saveCustomTranslation);
const cancelBtn = document.createElement('button');
cancelBtn.textContent = '取消';
cancelBtn.style.flex = '1';
cancelBtn.style.padding = '10px';
cancelBtn.style.border = '1px solid #ddd';
cancelBtn.style.borderRadius = '6px';
cancelBtn.style.backgroundColor = '#f5f5f5';
cancelBtn.style.color = '#333';
cancelBtn.style.fontWeight = '600';
cancelBtn.style.fontSize = '14px';
cancelBtn.style.cursor = 'pointer';
cancelBtn.style.transition = 'all 0.2s ease';
cancelBtn.addEventListener('mouseenter', () => {
cancelBtn.style.backgroundColor = '#e0e0e0';
});
cancelBtn.addEventListener('mouseleave', () => {
cancelBtn.style.backgroundColor = '#f5f5f5';
});
cancelBtn.addEventListener('click', () => {
originalInput.value = '';
translationInput.value = '';
categorySelect.value = categories[0].id;
});
formButtons.appendChild(saveBtn);
formButtons.appendChild(cancelBtn);
addForm.appendChild(formTitle);
addForm.appendChild(formFields);
addForm.appendChild(formButtons);
// 组装弹窗
modalContent.appendChild(searchContainer);
modalContent.appendChild(categoryContainer);
modalContent.appendChild(translationList);
modalContent.appendChild(addForm);
modal.appendChild(modalContent);
// 点击背景关闭
modal.addEventListener('click', async (e) => {
if (e.target === modal) {
modal.remove();
// 只有在有修改的情况下才刷新页面
if (window.translationManagerHasChanges) {
// 清除所有元素的翻译标记,确保重新翻译
document.querySelectorAll('[data-oao-translated="true"]').forEach(element => {
delete element.dataset.oaoTranslated;
});
await translateRoot(document);
// 重置标记
window.translationManagerHasChanges = false;
}
}
});
document.body.appendChild(modal);
// 搜索功能
searchInput.addEventListener('input', () => {
const searchTerm = searchInput.value.toLowerCase();
const translationItems = translationList.querySelectorAll('.translation-item');
translationItems.forEach(item => {
const originalText = item.querySelector('.original-text').textContent.toLowerCase();
const translationText = item.querySelector('.translation-text').textContent.toLowerCase();
if (originalText.includes(searchTerm) || translationText.includes(searchTerm)) {
item.style.display = 'flex';
} else {
item.style.display = 'none';
}
});
});
// 默认显示第一个分类
if (categories.length > 0) {
async function showFirstCategory() {
await ensureDictionaries();
const firstCategoryBtn = categoryContainer.querySelector(`[data-category="${categories[0].id}"]`);
if (firstCategoryBtn) {
firstCategoryBtn.click();
}
}
showFirstCategory();
}
}
// 显示指定分类的翻译
function showTranslationsByCategory(category) {
const translationList = document.getElementById('translation-list');
if (!translationList) return;
// 获取该分类的所有翻译(包括默认翻译和自定义翻译)
const translations = {};
// 添加默认翻译
if (dictionaryCache[category]) {
for (const [key, value] of Object.entries(dictionaryCache[category])) {
translations[key] = value.webpagedata;
}
}
// 添加或覆盖自定义翻译
if (customTranslations[category]) {
for (const [key, value] of Object.entries(customTranslations[category])) {
// 检查key是否为中文,如果是中文,跳过不添加
if (/[\u4e00-\u9fa5]/.test(key)) {
continue;
}
translations[key] = value;
}
}
// 显示翻译列表
translationList.innerHTML = '';
if (Object.keys(translations).length === 0) {
const emptyMessage = document.createElement('div');
emptyMessage.style.textAlign = 'center';
emptyMessage.style.color = '#999';
emptyMessage.style.padding = '20px';
emptyMessage.textContent = '暂无翻译数据';
translationList.appendChild(emptyMessage);
return;
}
// 按原文排序
const sortedKeys = Object.keys(translations).sort();
sortedKeys.forEach((key, index) => {
const translationItem = document.createElement('div');
translationItem.className = 'translation-item';
translationItem.style.display = 'flex';
translationItem.style.alignItems = 'center';
translationItem.style.justifyContent = 'space-between';
translationItem.style.padding = '10px';
translationItem.style.border = '1px solid #eee';
translationItem.style.borderRadius = '6px';
translationItem.style.marginBottom = '8px';
translationItem.style.backgroundColor = customTranslations[category] && customTranslations[category][key] ? '#f0f8ff' : '#fff';
const textContainer = document.createElement('div');
textContainer.style.flex = '1';
const translationDisplay = document.createElement('div');
translationDisplay.style.fontSize = '14px';
translationDisplay.style.display = 'flex';
translationDisplay.style.alignItems = 'center';
translationDisplay.style.gap = '10px';
const originalText = document.createElement('span');
originalText.className = 'original-text';
originalText.style.fontWeight = '500';
originalText.style.color = '#333';
originalText.textContent = key;
const arrow = document.createElement('span');
arrow.style.color = '#999';
arrow.textContent = '→';
const translationText = document.createElement('span');
translationText.className = 'translation-text';
translationText.style.color = '#666';
translationText.textContent = translations[key];
translationDisplay.appendChild(originalText);
translationDisplay.appendChild(arrow);
translationDisplay.appendChild(translationText);
textContainer.appendChild(translationDisplay);
const actionsContainer = document.createElement('div');
actionsContainer.style.display = 'flex';
actionsContainer.style.gap = '8px';
const editBtn = document.createElement('button');
editBtn.textContent = '编辑';
editBtn.style.padding = '6px 12px';
editBtn.style.border = '1px solid #027ba0ff';
editBtn.style.borderRadius = '4px';
editBtn.style.backgroundColor = '#fff';
editBtn.style.color = '#027ba0ff';
editBtn.style.fontSize = '12px';
editBtn.style.cursor = 'pointer';
editBtn.style.transition = 'all 0.2s ease';
editBtn.addEventListener('mouseenter', () => {
editBtn.style.backgroundColor = '#f0f8ff';
});
editBtn.addEventListener('mouseleave', () => {
editBtn.style.backgroundColor = '#fff';
});
// 编辑按钮点击事件处理器 - 使用IIFE创建闭包,确保key值正确
(function(currentKey, currentCategory, currentTranslations, currentTranslationText) {
// 定义编辑处理函数
const handleEdit = () => {
// log('info', '点击编辑按钮,原文:', currentKey, '当前翻译:', currentTranslations[currentKey]);
// 隐藏原翻译显示
translationDisplay.style.display = 'none';
// 创建编辑容器
const editContainer = document.createElement('div');
editContainer.className = 'edit-container';
editContainer.style.display = 'flex';
editContainer.style.alignItems = 'center';
editContainer.style.gap = '10px';
editContainer.style.width = '100%';
editContainer.style.padding = '8px';
editContainer.style.backgroundColor = '#f9f9f9';
editContainer.style.borderRadius = '4px';
// 显示英文
// console.log('=== 编辑容器创建 ===');
// console.log('currentKey:', currentKey);
// console.log('currentTranslations[currentKey]:', currentTranslations[currentKey]);
const originalSpan = document.createElement('span');
originalSpan.style.fontWeight = '500';
originalSpan.style.color = '#333';
originalSpan.style.whiteSpace = 'nowrap';
originalSpan.textContent = currentKey;
// console.log('originalSpan.textContent:', originalSpan.textContent);
// 显示箭头
const arrowSpan = document.createElement('span');
arrowSpan.style.color = '#999';
arrowSpan.textContent = '→';
// 显示原中文
const originalTranslationSpan = document.createElement('span');
originalTranslationSpan.style.color = '#999';
originalTranslationSpan.style.textDecoration = 'line-through';
originalTranslationSpan.textContent = currentTranslations[currentKey];
// 创建编辑框
const editInput = document.createElement('input');
editInput.type = 'text';
editInput.value = currentTranslations[currentKey];
editInput.style.padding = '4px 8px';
editInput.style.border = '1px solid #ddd';
editInput.style.borderRadius = '4px';
editInput.style.fontSize = '14px';
editInput.style.backgroundColor = '#f8f9fa';
editInput.style.color = '#333';
editInput.style.minWidth = '150px';
// 添加input事件监听器
editInput.addEventListener('input', function(e) {
// console.log('输入事件 - 当前值:', e.target.value);
});
// 创建新的保存按钮替换编辑按钮
// console.log('=== 开始创建保存按钮 ===');
// 创建新的保存按钮
const saveBtn = document.createElement('button');
saveBtn.textContent = '保存';
saveBtn.style.cssText = `
padding: 6px 12px;
border: 1px solid #027ba0ff;
border-radius: 4px;
background-color: #027ba0ff;
color: #ffffff;
font-size: 12px;
cursor: pointer;
width: 80px;
text-align: center;
display: inline-block;
`;
// console.log('创建的保存按钮textContent:', saveBtn.textContent);
// console.log('创建的保存按钮样式:', saveBtn.style.cssText);
// 保存原始编辑按钮的引用
const originalEditBtn = editBtn;
// 替换编辑按钮为保存按钮
actionsContainer.replaceChild(saveBtn, editBtn);
// console.log('保存按钮已替换编辑按钮');
// 保存按钮点击事件
saveBtn.onclick = async () => {
// log('info', '=== 保存按钮点击事件开始 ===');
const inputElement = textContainer.querySelector('.edit-container input');
// log('debug', '获取到的编辑框:', inputElement);
if (!inputElement) {
// log('error', '编辑框未找到');
showMessage('编辑框未找到', 'error');
return;
}
const newTranslation = inputElement.value.trim();
// log('info', '原文:', currentKey, '原始翻译:', currentTranslations[currentKey], '新翻译:', newTranslation);
if (newTranslation) {
// log('info', '开始保存翻译...');
// 保存翻译
const updatedCustomTranslations = JSON.parse(JSON.stringify(customTranslations));
// log('debug', '保存前 customTranslations:', customTranslations);
if (!updatedCustomTranslations[currentCategory]) {
updatedCustomTranslations[currentCategory] = {};
// log('info', '创建新分类:', currentCategory);
}
updatedCustomTranslations[currentCategory][currentKey] = newTranslation;
// log('info', '更新后的翻译:', updatedCustomTranslations[currentCategory][currentKey]);
setStorageValue(CUSTOM_TRANSLATIONS_KEY, updatedCustomTranslations);
customTranslations = updatedCustomTranslations;
// log('debug', '保存后 customTranslations:', customTranslations);
// 恢复显示
translationDisplay.style.display = 'flex';
currentTranslationText.textContent = newTranslation;
textContainer.removeChild(editContainer);
// log('info', '管理界面更新完成');
// 移除取消按钮
if (cancelBtn.parentNode === actionsContainer) {
actionsContainer.removeChild(cancelBtn);
}
// 恢复编辑按钮
saveBtn.textContent = '编辑';
saveBtn.style.backgroundColor = '#fff';
saveBtn.style.color = '#027ba0ff';
saveBtn.style.width = '60px';
// 重新绑定编辑事件
saveBtn.onclick = handleEdit;
showMessage('翻译保存成功!请手动刷新页面(按F5)查看最新翻译', 'success');
// 标记有修改
window.translationManagerHasChanges = true;
// log('info', '保存成功,设置标记为true');
try {
// 清除所有元素的翻译标记,确保重新翻译
const translatedElements = document.querySelectorAll('[data-oao-translated="true"]');
// log('info', '找到', translatedElements.length, '个已翻译元素');
translatedElements.forEach(element => {
delete element.dataset.oaoTranslated;
});
// log('info', '清除翻译标记完成');
// 立即刷新页面,显示最新翻译
// log('info', '开始刷新页面');
// 强制重新加载字典,确保包含最新的自定义翻译
const dictionaries = await ensureDictionaries();
// log('info', '重新加载字典完成');
// 直接调用futggTranslator.translate,确保使用最新的字典
if (window.location.hostname.includes('fut.gg')) {
const extraDictionaries = [
dictionaries.basic,
dictionaries.sixStat,
dictionaries.playstyles,
dictionaries.roles,
];
let evolutionDictionary = null;
const shouldApplyEvolutionFormat = evolutionFormat !== 2;
if (shouldApplyEvolutionFormat) {
evolutionDictionary = dictionaries.evolutions;
extraDictionaries.push(evolutionDictionary);
}
// futgg evolutions 页面需要始终包含 evolutionDictionary,用于特殊处理
const isFutggEvolutionsPage = window.location.href === 'https://www.fut.gg/evolutions/' ||
window.location.href.includes('fut.gg/evolutions');
if (isFutggEvolutionsPage && !evolutionDictionary) {
evolutionDictionary = dictionaries.evolutions;
extraDictionaries.push(evolutionDictionary);
}
// log('info', '调用 futggTranslator.translate');
futggTranslator.translate(
document,
dictionaries.futgg,
{
useShortenTerms: USE_SHORTEN_TERMS,
showOriginalWithBrackets: SHOW_ORIGINAL_WITH_BRACKETS,
extraDictionaries: extraDictionaries,
evolutionDictionary: evolutionDictionary
}
);
// log('info', 'futggTranslator 运行完成');
}
// log('info', '页面刷新完成');
} catch (error) {
// log('error', '刷新页面时出错:', error);
}
// 重新加载翻译列表
showTranslationsByCategory(currentCategory);
// log('info', '翻译列表重新加载完成');
} else {
showMessage('请输入翻译内容', 'error');
}
};
// 注意:editBtn是外部的const变量,不能重新赋值
// 我们直接使用saveBtn,不需要更新editBtn引用
// console.log('=== 保存按钮创建完成 ===');
// 清理已有的取消按钮
const existingCancelBtns = actionsContainer.querySelectorAll('.cancel-btn');
existingCancelBtns.forEach(btn => btn.remove());
// 取消按钮
const cancelBtn = document.createElement('button');
cancelBtn.className = 'cancel-btn';
cancelBtn.textContent = '取消';
cancelBtn.style.padding = '6px 12px';
cancelBtn.style.border = '1px solid #ddd';
cancelBtn.style.borderRadius = '4px';
cancelBtn.style.backgroundColor = '#fff';
cancelBtn.style.color = '#333';
cancelBtn.style.fontSize = '12px';
cancelBtn.style.cursor = 'pointer';
cancelBtn.addEventListener('click', () => {
// 恢复显示
translationDisplay.style.display = 'flex';
textContainer.removeChild(editContainer);
// 移除取消按钮
if (cancelBtn.parentNode === actionsContainer) {
actionsContainer.removeChild(cancelBtn);
}
// 恢复编辑按钮
saveBtn.textContent = '编辑';
saveBtn.style.backgroundColor = '#fff';
saveBtn.style.color = '#027ba0ff';
saveBtn.style.width = '60px';
// 重新绑定编辑事件
saveBtn.onclick = handleEdit;
});
// 保存按钮点击事件
saveBtn.onclick = () => {
// log('info', '=== 保存按钮点击事件开始 ===');
const inputElement = textContainer.querySelector('.edit-container input');
// log('debug', '获取到的编辑框:', inputElement);
if (!inputElement) {
// log('error', '编辑框未找到');
showMessage('编辑框未找到', 'error');
return;
}
const newTranslation = inputElement.value.trim();
// log('info', '原文:', currentKey, '原始翻译:', currentTranslations[currentKey], '新翻译:', newTranslation);
if (newTranslation) {
// log('info', '开始保存翻译...');
// 保存翻译
const updatedCustomTranslations = JSON.parse(JSON.stringify(customTranslations));
// log('debug', '保存前 customTranslations:', customTranslations);
if (!updatedCustomTranslations[currentCategory]) {
updatedCustomTranslations[currentCategory] = {};
// log('info', '创建新分类:', currentCategory);
}
updatedCustomTranslations[currentCategory][currentKey] = newTranslation;
// log('info', '更新后的翻译:', updatedCustomTranslations[currentCategory][currentKey]);
setStorageValue(CUSTOM_TRANSLATIONS_KEY, updatedCustomTranslations);
customTranslations = updatedCustomTranslations;
// log('debug', '保存后 customTranslations:', customTranslations);
// 恢复显示
translationDisplay.style.display = 'flex';
currentTranslationText.textContent = newTranslation;
textContainer.removeChild(editContainer);
// log('info', '管理界面更新完成');
// 移除取消按钮
if (cancelBtn.parentNode === actionsContainer) {
actionsContainer.removeChild(cancelBtn);
}
// 恢复编辑按钮
editBtn.textContent = '编辑';
editBtn.style.backgroundColor = '#fff';
editBtn.style.color = '#027ba0ff';
// 重新绑定编辑事件
editBtn.onclick = handleEdit;
showMessage('翻译保存成功!请手动刷新页面(按F5)查看最新翻译', 'success');
// 标记有修改
window.translationManagerHasChanges = true;
// log('info', '保存成功,设置标记为true');
// 重新加载翻译列表
showTranslationsByCategory(currentCategory);
// log('info', '翻译列表重新加载完成');
try {
// 清除所有元素的翻译标记,确保重新翻译
const translatedElements = document.querySelectorAll('[data-oao-translated="true"]');
// log('info', '找到', translatedElements.length, '个已翻译元素');
translatedElements.forEach(element => {
delete element.dataset.oaoTranslated;
});
// log('info', '清除翻译标记完成');
// 重新翻译页面
// log('info', '开始刷新页面');
translateRoot(document);
// log('info', '页面刷新完成');
} catch (error) {
// log('error', '刷新页面时出错:', error);
}
} else {
showMessage('请输入翻译内容', 'error');
}
};
// 组装编辑容器
editContainer.appendChild(originalSpan);
editContainer.appendChild(arrowSpan);
editContainer.appendChild(originalTranslationSpan);
editContainer.appendChild(editInput);
// 添加到文本容器
textContainer.appendChild(editContainer);
// 添加取消按钮到操作容器
actionsContainer.appendChild(cancelBtn);
// 聚焦编辑框
editInput.focus();
};
// 绑定编辑事件
editBtn.onclick = handleEdit;
})(key, category, translations, translationText);
const deleteBtn = document.createElement('button');
deleteBtn.textContent = '恢复原始';
deleteBtn.style.padding = '6px 12px';
deleteBtn.style.border = '1px solid #dc3545';
deleteBtn.style.borderRadius = '4px';
deleteBtn.style.backgroundColor = '#fff';
deleteBtn.style.color = '#dc3545';
deleteBtn.style.fontSize = '12px';
deleteBtn.style.cursor = 'pointer';
deleteBtn.style.transition = 'all 0.2s ease';
deleteBtn.addEventListener('mouseenter', () => {
deleteBtn.style.backgroundColor = '#f8d7da';
});
deleteBtn.addEventListener('mouseleave', () => {
deleteBtn.style.backgroundColor = '#fff';
});
deleteBtn.addEventListener('click', async () => {
// log('info', '点击恢复原始按钮,原文:', key, '当前翻译:', translations[key]);
if (confirm('确定要恢复原始翻译吗?')) {
if (customTranslations[category]) {
// log('info', '开始恢复原始翻译...');
// log('debug', '恢复前 customTranslations:', customTranslations);
delete customTranslations[category][key];
// log('debug', '删除翻译后 customTranslations:', customTranslations);
if (Object.keys(customTranslations[category]).length === 0) {
delete customTranslations[category];
// log('info', '删除空分类:', category);
}
setStorageValue(CUSTOM_TRANSLATIONS_KEY, customTranslations);
// log('debug', '保存后 customTranslations:', customTranslations);
showTranslationsByCategory(category);
// log('info', '翻译列表重新加载完成');
showMessage('已恢复原始翻译!请手动刷新页面(按F5)查看最新翻译', 'success');
// 标记有修改
window.translationManagerHasChanges = true;
// log('info', '恢复原始成功,设置标记为true');
try {
// 清除所有元素的翻译标记,确保重新翻译
const translatedElements = document.querySelectorAll('[data-oao-translated="true"]');
// log('info', '找到', translatedElements.length, '个已翻译元素');
translatedElements.forEach(element => {
delete element.dataset.oaoTranslated;
});
// log('info', '清除翻译标记完成');
// 立即刷新页面,显示原始翻译
// log('info', '开始刷新页面');
await translateRoot(document);
// log('info', '页面刷新完成');
} catch (error) {
// log('error', '刷新页面时出错:', error);
}
}
}
});
// 只有自定义翻译可以删除
if (customTranslations[category] && customTranslations[category][key]) {
actionsContainer.appendChild(editBtn);
actionsContainer.appendChild(deleteBtn);
} else {
// 默认翻译只能编辑(添加为自定义翻译)
actionsContainer.appendChild(editBtn);
}
translationItem.appendChild(textContainer);
translationItem.appendChild(actionsContainer);
translationList.appendChild(translationItem);
});
}
// 保存自定义翻译
function saveCustomTranslation() {
const originalText = document.getElementById('original-text').value.trim();
const translationText = document.getElementById('translation-text').value.trim();
const category = document.getElementById('category-select').value;
if (!originalText || !translationText) {
showMessage('请输入原文和翻译', 'error');
return;
}
// 检查原文是否为中文,如果是则提示用户输入英文
if (/[\u4e00-\u9fa5]/.test(originalText)) {
showMessage('原文必须是英文,不能是中文', 'error');
return;
}
// 确保分类对象存在
if (!customTranslations[category]) {
customTranslations[category] = {};
}
// 保存翻译
customTranslations[category][originalText] = translationText;
// 保存到本地存储
setStorageValue(CUSTOM_TRANSLATIONS_KEY, customTranslations);
// 更新翻译列表
showTranslationsByCategory(category);
// 清空表单
document.getElementById('original-text').value = '';
document.getElementById('translation-text').value = '';
showMessage('翻译保存成功!请手动刷新页面(按F5)查看最新翻译', 'success');
// 重新翻译页面
translateRoot(document);
}
// 显示消息提示
function showMessage(message, type = 'info') {
// 创建消息元素
const messageElement = document.createElement('div');
messageElement.style.position = 'fixed';
messageElement.style.top = '20px';
messageElement.style.right = '20px';
messageElement.style.padding = '12px 20px';
messageElement.style.borderRadius = '8px';
messageElement.style.color = '#fff';
messageElement.style.fontWeight = '600';
messageElement.style.zIndex = '99999';
messageElement.style.boxShadow = '0 4px 12px rgba(0, 0, 0, 0.2)';
messageElement.style.transition = 'all 0.3s ease';
messageElement.style.transform = 'translateX(400px)';
messageElement.textContent = message;
// 根据类型设置颜色
switch (type) {
case 'success':
messageElement.style.backgroundColor = '#28a745';
break;
case 'error':
messageElement.style.backgroundColor = '#dc3545';
break;
case 'info':
default:
messageElement.style.backgroundColor = '#027ba0ff';
break;
}
// 添加到页面
document.body.appendChild(messageElement);
// 显示动画
setTimeout(() => {
messageElement.style.transform = 'translateX(0)';
}, 100);
// 3秒后消失
setTimeout(() => {
messageElement.style.transform = 'translateX(400px)';
setTimeout(() => {
if (document.body.contains(messageElement)) {
document.body.removeChild(messageElement);
}
}, 300);
}, 3000);
}
const evolutionFormatBtn = document.createElement('button');
evolutionFormatBtn.className = `${SIDEBAR_BUTTON_CLASS} ${SIDEBAR_BUTTON_CLASS}--evolution-format`;
// 根据当前状态设置按钮文本和样式
function updateEvolutionFormatButton() {
switch (evolutionFormat) {
case 0:
evolutionFormatBtn.textContent = '进化格式: 中英';
evolutionFormatBtn.className = `${SIDEBAR_BUTTON_CLASS} ${SIDEBAR_BUTTON_CLASS}--evolution-format`;
evolutionFormatBtn.style.backgroundColor = '#049c04ff'; // 保持黄色
evolutionFormatBtn.style.color = '#FFFFFF';
break;
case 1:
evolutionFormatBtn.textContent = '进化格式: 中文';
evolutionFormatBtn.className = `${SIDEBAR_BUTTON_CLASS} ${SIDEBAR_BUTTON_CLASS}--evolution-format chinese-only`;
evolutionFormatBtn.style.backgroundColor = '#049c04ff'; // 保持黄色
evolutionFormatBtn.style.color = '#FFFFFF';
break;
case 2:
evolutionFormatBtn.textContent = '进化格式: 英文';
evolutionFormatBtn.className = `${SIDEBAR_BUTTON_CLASS} ${SIDEBAR_BUTTON_CLASS}--evolution-format english-only`;
evolutionFormatBtn.style.backgroundColor = '#049c04ff'; // 保持黄色
evolutionFormatBtn.style.color = '#FFFFFF';
break;
}
}
updateEvolutionFormatButton();
evolutionFormatBtn.addEventListener('click', () => {
// 循环切换状态: 0 → 1 → 2 → 0
evolutionFormat = (evolutionFormat + 1) % 3;
// 保存状态到本地存储
setStorageValue(EVOLUTION_FORMAT_KEY, evolutionFormat);
// 更新按钮文本和样式
updateEvolutionFormatButton();
// 无论切换到哪个状态,都刷新页面以确保格式更新
location.reload();
});
// 添加翻译管理按钮
const translationManagerBtn = document.createElement('button');
translationManagerBtn.className = `${SIDEBAR_BUTTON_CLASS} ${SIDEBAR_BUTTON_CLASS}--translation-manager`;
translationManagerBtn.textContent = '翻译管理';
translationManagerBtn.style.backgroundColor = '#9c27b0'; // 紫色
translationManagerBtn.style.color = '#FFFFFF';
translationManagerBtn.addEventListener('click', openTranslationManager);
// 添加隐藏按钮
const hideBtn = document.createElement('button');
hideBtn.className = `${SIDEBAR_BUTTON_CLASS} ${SIDEBAR_BUTTON_CLASS}--hide`;
hideBtn.style.backgroundColor = '#4800bdff'; // 紫色
hideBtn.style.color = '#FFFFFF';
hideBtn.textContent = '隐藏侧边栏';
hideBtn.addEventListener('click', () => {
// 隐藏侧边栏面板
panel.style.display = 'none';
// 保存隐藏状态
setStorageValue(SIDEBAR_HIDDEN_KEY, true);
// 显示可拖动的悬浮图标
createFloatIcon();
});
const label = document.createElement('div');
label.className = 'oaevo-sidebar-label';
label.textContent = '';
// 设置按钮颜色
refreshBtn.style.backgroundColor = '#FF0000'; // 红色
refreshBtn.style.color = '#FFFFFF';
if (typeof settingsBtn !== 'undefined') {
settingsBtn.style.backgroundColor = '#FF7F00'; // 橙色
settingsBtn.style.color = '#FFFFFF';
}
if (typeof expandBtn !== 'undefined') {
expandBtn.style.backgroundColor = '#FF7F00'; // 橙色
expandBtn.style.color = '#FFFFFF';
}
if (typeof evolutionFormatBtn !== 'undefined') {
evolutionFormatBtn.style.backgroundColor = '#049c04ff'; // 绿
evolutionFormatBtn.style.color = '#FFFFFF';
}
if (typeof featureBtn !== 'undefined') {
featureBtn.style.backgroundColor = '#027ba0ff'; // 蓝
featureBtn.style.color = '#FFFFFF';
}
// 添加"关注欧啊欧"按钮
const followBtn = document.createElement('button');
followBtn.className = `${SIDEBAR_BUTTON_CLASS} ${SIDEBAR_BUTTON_CLASS}--follow`;
followBtn.textContent = '关注欧啊欧';
followBtn.style.backgroundColor = '#FF69B4'; // 粉色
followBtn.style.color = '#FFFFFF';
followBtn.style.cursor = 'default'; // 不可点击的鼠标样式
// 创建悬浮窗
const hoverWindow = document.createElement('div');
hoverWindow.style.position = 'absolute';
hoverWindow.style.left = '-320px'; // 在按钮左侧显示,放大后调整位置
hoverWindow.style.top = '0';
hoverWindow.style.width = '300px'; // 宽度放大1倍
hoverWindow.style.backgroundColor = '#fff';
hoverWindow.style.borderRadius = '8px';
hoverWindow.style.boxShadow = '0 4px 12px rgba(0,0,0,0.3)';
hoverWindow.style.padding = '10px';
hoverWindow.style.display = 'none';
hoverWindow.style.zIndex = '10000';
// 添加图片到悬浮窗
const hoverImage = document.createElement('img');
hoverImage.src = 'https://s41.ax1x.com/2026/03/01/peS2q8f.png';
hoverImage.style.width = '100%';
hoverImage.style.borderRadius = '4px';
// 图片加载失败时显示文字信息
hoverImage.onerror = function() {
// 移除图片
hoverImage.style.display = 'none';
// 创建文字提示
const textTip = document.createElement('div');
textTip.textContent = '微信搜索"我要欧啊欧"';
textTip.style.fontSize = '20px';
textTip.style.fontWeight = 'bold';
textTip.style.color = '#333';
textTip.style.textAlign = 'center';
textTip.style.padding = '20px';
hoverWindow.appendChild(textTip);
};
hoverWindow.appendChild(hoverImage);
// 鼠标悬停显示悬浮窗
followBtn.addEventListener('mouseenter', () => {
hoverWindow.style.display = 'block';
});
// 鼠标离开隐藏悬浮窗
followBtn.addEventListener('mouseleave', () => {
hoverWindow.style.display = 'none';
});
// 将悬浮窗添加到按钮
followBtn.style.position = 'relative';
followBtn.appendChild(hoverWindow);
// 重新排列按钮顺序
panel.appendChild(title);
// 翻译开关按钮(第二个位置)
if (window.location.hostname.includes('easysbc.io')) {
// EASYSBC页面:EASY翻译按钮
if (typeof easysbcBtn !== 'undefined') {
panel.appendChild(easysbcBtn);
}
} else if (!window.location.hostname.includes('fut.gg')) {
// FUTBIN页面:FUTBIN翻译按钮
if (typeof expandBtn !== 'undefined') {
panel.appendChild(expandBtn);
}
} else {
// FUTGG页面:FUTGG翻译按钮(settingsBtn)
if (typeof settingsBtn !== 'undefined') {
panel.appendChild(settingsBtn);
}
}
// 其他按钮
panel.appendChild(refreshBtn);
panel.appendChild(evolutionFormatBtn);
panel.appendChild(featureBtn);
panel.appendChild(translationManagerBtn);
panel.appendChild(hideBtn);
panel.appendChild(followBtn);
// 添加版本信息
const versionInfo = document.createElement('div');
versionInfo.setAttribute('data-version-indicator', 'true');
// 从 UserScript 头部读取版本号
let scriptVersion = 'unknown';
try {
// 方法1: 从 GM_info 获取(Tampermonkey/Violentmonkey 支持)
if (typeof GM_info !== 'undefined' && GM_info.script && GM_info.script.version) {
scriptVersion = GM_info.script.version;
} else {
// 方法2: 从当前脚本内容解析
const scriptContent = document.querySelector('script[id^="tampermonkey"], script[id^="violentmonkey"]');
if (scriptContent) {
const versionMatch = scriptContent.textContent.match(/@version\s+([\d.]+)/);
if (versionMatch) {
scriptVersion = versionMatch[1];
}
}
}
} catch (e) {
// console.warn('Failed to get script version:', e);
}
versionInfo.innerHTML = 'version: ' + scriptVersion;
versionInfo.style.fontSize = '12px';
versionInfo.style.color = '#fff';
versionInfo.style.textAlign = 'center';
versionInfo.style.marginTop = '8px';
versionInfo.style.opacity = '0.7';
versionInfo.style.cursor = 'pointer';
versionInfo.title = '点击检查更新';
// 检查是否有新版本信息
const savedNewVersion = localStorage.getItem(NEW_VERSION_KEY);
if (savedNewVersion) {
try {
newVersionInfo = JSON.parse(savedNewVersion);
// 添加红点
const dot = document.createElement('span');
dot.className = 'version-update-dot';
dot.style.cssText = `
display: inline-block;
width: 8px;
height: 8px;
background: #ff4444;
border-radius: 50%;
margin-left: 6px;
animation: pulse 2s infinite;
`;
versionInfo.appendChild(dot);
} catch (e) {
// 解析失败,忽略
}
}
// 点击版本信息时显示更新弹窗
versionInfo.addEventListener('click', () => {
if (newVersionInfo) {
showUpdateDialog(newVersionInfo.remoteVersion, newVersionInfo.currentVersion);
} else {
// 没有新版本信息,执行一次检查
checkVersionUpdate('popup');
}
});
panel.appendChild(versionInfo);
panel.appendChild(label);
sidebar.appendChild(panel);
// 确保 body 存在
if (!document.body) {
// 如果 body 不存在,延迟创建
setTimeout(() => createSidebar(), 100);
return;
}
document.body.appendChild(sidebar);
let isDragging = false;
let startX, startY, initialRight, initialTop;
// 开始拖动的通用函数
function startSidebarDrag(e) {
if (e.target === panel || e.target === title) {
isDragging = true;
sidebar.classList.add('dragging');
const clientX = e.clientX || e.touches[0].clientX;
const clientY = e.clientY || e.touches[0].clientY;
startX = clientX;
startY = clientY;
const rect = sidebar.getBoundingClientRect();
initialRight = window.innerWidth - rect.right;
initialTop = rect.top;
e.preventDefault();
}
}
// 移动的通用函数
function moveSidebarDrag(e) {
if (!isDragging) return;
const clientX = e.clientX || e.touches[0].clientX;
const clientY = e.clientY || e.touches[0].clientY;
const deltaX = clientX - startX;
const deltaY = clientY - startY;
const newRight = Math.max(0, initialRight - deltaX);
const newTop = Math.max(0, initialTop + deltaY);
sidebar.style.right = newRight + 'px';
sidebar.style.top = newTop + 'px';
sidebar.style.left = 'auto';
}
// 结束拖动的通用函数
function endSidebarDrag() {
if (isDragging) {
isDragging = false;
sidebar.classList.remove('dragging');
}
}
// 鼠标事件
panel.addEventListener('mousedown', startSidebarDrag);
document.addEventListener('mousemove', moveSidebarDrag);
document.addEventListener('mouseup', endSidebarDrag);
// 触摸事件(支持手机端)
panel.addEventListener('touchstart', startSidebarDrag, { passive: false });
document.addEventListener('touchmove', moveSidebarDrag, { passive: false });
document.addEventListener('touchend', endSidebarDrag);
}
// 字典缓存(初始化为空,通过 loadBuiltInDictionaries 加载)
const dictionaryCache = {
basic: null,
sixStat: null,
roles: null,
squad: null,
chemistry: null,
playstyles: null,
evolutions: null,
futgg: null,
futbin: null,
easysbc: null
};
let evolutionsLoadPromise = null;
let builtInDictionariesLoadPromise = null;
// 工具函数
function normalizeTranslationDictionary(rawDict) {
const normalized = {};
if (!rawDict) {
return normalized;
}
Object.entries(rawDict).forEach(([english, entry]) => {
if (!english) {
return;
}
// 支持多种格式
const webpagedata = String(entry?.webpagedata || entry?.formal || entry?.professional || entry?.CN || '').trim();
const shorten = String(entry?.shorten || entry?.casual || entry?.playerSlang || entry?.CN || webpagedata).trim();
normalized[english.trim()] = {
webpagedata,
shorten: shorten || webpagedata,
};
});
return normalized;
}
function buildCaseInsensitiveMap(dictionary) {
const map = {};
if (dictionary && typeof dictionary === 'object') {
for (const key of Object.keys(dictionary)) {
map[key.toUpperCase()] = dictionary[key];
}
}
return map;
}
function isCacheEntryValid(entry) {
return (
entry &&
typeof entry.timestamp === 'number' &&
entry.data &&
Date.now() - entry.timestamp < THREE_HOURS_MS
);
}
// 获取缓存
function getStorageValue(key) {
try {
const value = GM_getValue(key, null);
return value ? JSON.parse(value) : null;
} catch (error) {
// console.warn('Failed to get storage value:', error);
return null;
}
}
// 设置缓存
function setStorageValue(key, value) {
try {
GM_setValue(key, JSON.stringify(value));
} catch (error) {
// console.warn('Failed to set storage value:', error);
}
}
// 在线获取JSON(支持多数据源)
function fetchRemoteJson(url) {
// console.log('[OAO] 尝试从URL获取:', url);
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: 'GET',
url: url,
headers: {
'Cache-Control': 'no-cache'
},
onload: function(response) {
// console.log('[OAO] 请求完成,状态码:', response.status);
if (response.status === 200) {
try {
const data = JSON.parse(response.responseText);
// console.log('[OAO] JSON解析成功');
resolve(data);
} catch (error) {
// console.error('[OAO] 解析数据失败:', error);
// console.log('[OAO] 响应内容:', response.responseText);
reject(new Error('Failed to parse JSON'));
}
} else {
// console.error('[OAO] 请求失败,状态码:', response.status);
// console.log('[OAO] 响应内容:', response.responseText);
reject(new Error(`Request failed with status ${response.status}`));
}
},
onerror: function(error) {
// console.error('[OAO] 网络错误:', error);
reject(new Error('Network error'));
}
});
});
}
async function fetchRemoteJsonWithFallback(urls) {
// console.log('[OAO] 开始尝试多个数据源,共', urls.length, '个');
for (let i = 0; i < urls.length; i++) {
const _u = urls[i];
// console.log('[OAO] 尝试数据源', i + 1, ':', _u);
try {
const _dt = await fetchRemoteJson(_u);
let _fd = _dt;
let _sk = false;
if (typeof _dt === 'string' && _dt.startsWith('OAO-ENCRYPTED:')) {
const _ed = _dt.substring('OAO-ENCRYPTED:'.length);
const _dd = _d0(_ed);
if (_dd) {
_fd = _dd;
} else {
_sk = true;
}
}
if (_sk) {
// console.log('[OAO] 数据源解密失败,继续尝试下一个');
continue;
}
// console.log('[OAO] 成功从数据源', i + 1, '获取数据');
return _fd;
} catch (error) {
// console.error('[OAO] 数据源', i + 1, '失败:', error);
}
}
// console.error('[OAO] 所有数据源都失败了');
throw new Error('All data sources failed');
}
// 加载进化字典
async function loadEvolutionsDictionary() {
if (dictionaryCache.evolutions && Object.keys(dictionaryCache.evolutions).length > 0) {
return dictionaryCache.evolutions;
}
if (!evolutionsLoadPromise) {
evolutionsLoadPromise = (async () => {
try {
// 检查缓存
const cachedEntry = getStorageValue(EVOLUTIONS_STORAGE_KEY);
if (isCacheEntryValid(cachedEntry)) {
dictionaryCache.evolutions = normalizeTranslationDictionary(cachedEntry.data);
return dictionaryCache.evolutions;
}
// 尝试从远程获取(带备用数据源)
try {
const remoteJson = await fetchRemoteJsonWithFallback(EVOLUTIONS_DICT_URLS);
const normalized = normalizeTranslationDictionary(remoteJson);
// 保存到缓存
setStorageValue(EVOLUTIONS_STORAGE_KEY, {
timestamp: Date.now(),
data: remoteJson
});
dictionaryCache.evolutions = normalized;
return normalized;
} catch (error) {
dictionaryCache.evolutions = {};
return {};
}
} catch (error) {
dictionaryCache.evolutions = {};
return {};
} finally {
evolutionsLoadPromise = null;
}
})();
}
return evolutionsLoadPromise;
}
// 强制刷新进化字典
async function forceRefreshEvolutionsDictionary() {
try {
const remoteJson = await fetchRemoteJsonWithFallback(EVOLUTIONS_DICT_URLS);
const normalized = normalizeTranslationDictionary(remoteJson);
// 保存到缓存
setStorageValue(EVOLUTIONS_STORAGE_KEY, {
timestamp: Date.now(),
data: remoteJson
});
dictionaryCache.evolutions = normalized;
return normalized;
} catch (error) {
throw error;
}
}
// 加载内置翻译字典
async function loadBuiltInDictionaries() {
// console.log('[OAO] loadBuiltInDictionaries 被调用');
// 检查字典是否已加载且不为空
const hasBasic = dictionaryCache.basic && Object.keys(dictionaryCache.basic).length > 0;
const hasSixStat = dictionaryCache.sixStat && Object.keys(dictionaryCache.sixStat).length > 0;
if (hasBasic && hasSixStat) {
// console.log('[OAO] 使用已缓存的字典');
return {
basic: dictionaryCache.basic,
sixStat: dictionaryCache.sixStat,
roles: dictionaryCache.roles,
squad: dictionaryCache.squad,
chemistry: dictionaryCache.chemistry,
playstyles: dictionaryCache.playstyles,
futgg: dictionaryCache.futgg,
futbin: dictionaryCache.futbin,
easysbc: dictionaryCache.easysbc
};
}
if (!builtInDictionariesLoadPromise) {
builtInDictionariesLoadPromise = (async () => {
try {
// console.log('[OAO] 开始加载内置翻译字典');
// 检查缓存
const cachedEntry = getStorageValue(BUILT_IN_DICTIONARIES_STORAGE_KEY);
// console.log('[OAO] 缓存检查结果:', cachedEntry ? '有缓存' : '无缓存');
if (isCacheEntryValid(cachedEntry)) {
// console.log('[OAO] 使用缓存的字典');
const data = cachedEntry.data;
dictionaryCache.basic = data.basic;
dictionaryCache.sixStat = data.sixStat;
dictionaryCache.roles = data.roles;
dictionaryCache.squad = data.squad;
dictionaryCache.chemistry = data.chemistry;
dictionaryCache.playstyles = data.playstyles;
dictionaryCache.futgg = data.futgg;
dictionaryCache.futbin = data.futbin;
dictionaryCache.easysbc = data.easysbc;
dictionaryCache.sixStatLowerMap = buildCaseInsensitiveMap(data.sixStat);
return data;
}
// 尝试从远程获取(带备用数据源)
try {
// console.log('[OAO] 尝试从远程加载字典');
const remoteJson = await fetchRemoteJsonWithFallback(BUILT_IN_DICTIONARIES_URLS);
// console.log('[OAO] 远程加载成功');
// 保存到缓存
setStorageValue(BUILT_IN_DICTIONARIES_STORAGE_KEY, {
timestamp: Date.now(),
data: remoteJson
});
dictionaryCache.basic = remoteJson.basic;
dictionaryCache.sixStat = remoteJson.sixStat;
dictionaryCache.roles = remoteJson.roles;
dictionaryCache.squad = remoteJson.squad;
dictionaryCache.chemistry = remoteJson.chemistry;
dictionaryCache.playstyles = remoteJson.playstyles;
dictionaryCache.futgg = remoteJson.futgg;
dictionaryCache.futbin = remoteJson.futbin;
dictionaryCache.easysbc = remoteJson.easysbc;
dictionaryCache.sixStatLowerMap = buildCaseInsensitiveMap(remoteJson.sixStat);
return remoteJson;
} catch (error) {
// console.error('[OAO] 远程加载失败,使用空字典:', error);
// 如果远程加载失败,使用内置默认值
return setupDefaultBuiltInDictionaries();
}
} catch (error) {
// console.error('[OAO] 加载内置字典出错,使用空字典:', error);
// 出错时使用内置默认值
return setupDefaultBuiltInDictionaries();
} finally {
builtInDictionariesLoadPromise = null;
}
})();
}
return builtInDictionariesLoadPromise;
}
// 设置默认的内置翻译字典(备用)
function setupDefaultBuiltInDictionaries() {
const defaultDicts = {
basic: {},
sixStat: {},
roles: {},
squad: {},
chemistry: {},
playstyles: {},
futgg: {},
futbin: {},
easysbc: {}
};
dictionaryCache.basic = defaultDicts.basic;
dictionaryCache.sixStat = defaultDicts.sixStat;
dictionaryCache.roles = defaultDicts.roles;
dictionaryCache.squad = defaultDicts.squad;
dictionaryCache.chemistry = defaultDicts.chemistry;
dictionaryCache.playstyles = defaultDicts.playstyles;
dictionaryCache.futgg = defaultDicts.futgg;
dictionaryCache.futbin = defaultDicts.futbin;
dictionaryCache.easysbc = defaultDicts.easysbc;
dictionaryCache.sixStatLowerMap = buildCaseInsensitiveMap(defaultDicts.sixStat);
return defaultDicts;
}
// 确保所有字典加载完成
async function ensureDictionaries() {
// console.log('[OAO] ensureDictionaries 开始');
// console.log('[OAO] 当前 dictionaryCache 状态:', {
// basic: dictionaryCache.basic ? (Object.keys(dictionaryCache.basic).length > 0 ? Object.keys(dictionaryCache.basic).length + ' 条' : '空对象') : '未加载',
// sixStat: dictionaryCache.sixStat ? (Object.keys(dictionaryCache.sixStat).length > 0 ? Object.keys(dictionaryCache.sixStat).length + ' 条' : '空对象') : '未加载',
// evolutions: dictionaryCache.evolutions ? (Object.keys(dictionaryCache.evolutions).length > 0 ? Object.keys(dictionaryCache.evolutions).length + ' 条' : '空对象') : '未加载'
// });
if (!dictionaryCache.evolutions || Object.keys(dictionaryCache.evolutions).length === 0) {
// console.log('[OAO] 加载进化字典');
await loadEvolutionsDictionary();
}
// 检查是否需要加载内置字典(不存在或为空对象)
const needLoadBuiltIn = !dictionaryCache.basic || !dictionaryCache.sixStat || !dictionaryCache.easysbc ||
Object.keys(dictionaryCache.basic).length === 0 ||
Object.keys(dictionaryCache.sixStat).length === 0 ||
Object.keys(dictionaryCache.easysbc).length === 0;
if (needLoadBuiltIn) {
// console.log('[OAO] 加载内置翻译字典');
await loadBuiltInDictionaries();
}
// 合并自定义翻译到字典缓存 - 使用深拷贝避免修改原始缓存
const mergedDictionaries = JSON.parse(JSON.stringify(dictionaryCache));
// 确保sixStatLowerMap存在,用于大小写不敏感查找
if (!mergedDictionaries.sixStatLowerMap) {
mergedDictionaries.sixStatLowerMap = buildCaseInsensitiveMap(mergedDictionaries.sixStat);
}
// 确保customTranslations存在且是对象
if (customTranslations && typeof customTranslations === 'object') {
for (const [category, translations] of Object.entries(customTranslations)) {
if (translations && typeof translations === 'object') {
if (!mergedDictionaries[category]) {
mergedDictionaries[category] = {};
}
for (const [key, value] of Object.entries(translations)) {
// 覆盖默认翻译
mergedDictionaries[category][key] = {
webpagedata: value,
shorten: value // 简化版也使用相同翻译
};
}
}
}
}
return mergedDictionaries;
}
// 检查元素是否在翻译管理弹窗内
function isInTranslationModal(element) {
return element.closest('.oaevo-translation-modal') !== null;
}
// 基础信息翻译器
const baseInfoTranslator = {
translate: function(root, dictionary, options) {
if (!dictionary) return;
// 优化选择器,只选择可能包含文本的元素
const elements = root.querySelectorAll('div, span, p, h1, h2, h3, h4, h5, h6, a, li, button, label, th, td');
elements.forEach(element => {
// 跳过已翻译的元素,避免重复翻译
if (element.dataset.oaoTranslated === 'true') {
return;
}
// 跳过侧边栏按钮,避免翻译按钮文字
if (element.classList.contains('oaevo-sidebar-btn')) {
return;
}
// 跳过翻译管理弹窗内的元素
if (isInTranslationModal(element)) {
return;
}
// 跳过 EASYSBC evolutions/players 页面的特殊 HTML 格式元素
// 这些元素由 easysbcTranslator 专门处理
const isEasysbcPage = window.location.hostname.includes('easysbc.io');
if (isEasysbcPage) {
// 检查是否是 evolutions/players 页面
if (window.location.href.includes('/evolutions/players')) {
const closestEvoItem = element.closest('div.bg-evolution.h-6');
if (closestEvoItem) {
return;
}
}
// 检查是否是 evolutions/builder/*** 页面
if (window.location.href.includes('/evolutions/builder/')) {
const closestEvoItem = element.closest('div.bg-evolution.box-border');
if (closestEvoItem) {
return;
}
}
}
// 处理单个文本节点的情况
if (element.childNodes.length === 1 && element.firstChild.nodeType === Node.TEXT_NODE) {
const text = element.textContent.trim();
const textUpper = text.toUpperCase();
if (dictionary[text] || dictionary[textUpper]) {
const key = dictionary[text] ? text : textUpper;
const translation = options.useShortenTerms ? dictionary[key].shorten : dictionary[key].webpagedata;
element.textContent = options.showOriginalWithBrackets ? `${translation} (${text})` : translation;
// 标记为已翻译,避免重复翻译
element.dataset.oaoTranslated = 'true';
}
}
// 处理带有 SVG 图标的元素
else {
// 检查是否包含进化相关的子元素,跳过父元素避免重复翻译
const hasEvolutionBadge = Array.from(element.children).some(child =>
child.classList.contains('evolution-badge') || child.classList.contains('evo-box-req')
);
if (hasEvolutionBadge) {
return;
}
const textNodes = [];
element.childNodes.forEach(child => {
if (child.nodeType === Node.TEXT_NODE) {
textNodes.push(child.textContent.trim());
}
});
const text = textNodes.join(' ').trim();
const textUpper = text.toUpperCase();
if (text && (dictionary[text] || dictionary[textUpper])) {
const key = dictionary[text] ? text : textUpper;
const translation = options.useShortenTerms ? dictionary[key].shorten : dictionary[key].webpagedata;
let textIndex = 0;
element.childNodes.forEach(child => {
if (child.nodeType === Node.TEXT_NODE) {
if (textIndex === 0) {
child.textContent = translation;
} else {
child.textContent = '';
}
textIndex++;
}
});
// 标记为已翻译,避免重复翻译
element.dataset.oaoTranslated = 'true';
}
}
});
}
};
// 六维属性翻译器
const sixStatTranslator = {
translate: function(root, dictionary, options) {
if (!dictionary) return;
// 检查是否是 evo-lab/evolutions 页面
const isEvolutionsPage = window.location.href.includes('evo-lab/evolutions');
// 优化选择器,只选择可能包含文本的元素
const elements = root.querySelectorAll('div, span, p, h1, h2, h3, h4, h5, h6, a, li, button, label, th, td');
elements.forEach(element => {
// 跳过已翻译的元素,避免重复翻译
if (element.dataset.oaoTranslated === 'true') {
return;
}
// 跳过侧边栏按钮,避免翻译按钮文字
if (element.classList.contains('oaevo-sidebar-btn')) {
return;
}
// 跳过翻译管理弹窗内的元素
if (isInTranslationModal(element)) {
return;
}
// 跳过 EASYSBC evolutions/players 页面的特殊 HTML 格式元素
// 这些元素由 easysbcTranslator 专门处理
const isEasysbcPage = window.location.hostname.includes('easysbc.io');
if (isEasysbcPage) {
// 检查是否是 evolutions/players 页面
if (window.location.href.includes('/evolutions/players')) {
const closestEvoItem = element.closest('div.bg-evolution.h-6');
if (closestEvoItem) {
return;
}
}
// 检查是否是 evolutions/builder/*** 页面
if (window.location.href.includes('/evolutions/builder/')) {
const closestEvoItem = element.closest('div.bg-evolution.box-border');
if (closestEvoItem) {
return;
}
}
}
// 在 evo-lab/evolutions 页面跳过球员六项数据(速度、射门、传球、盘带、防守、身体)
if (isEvolutionsPage && (element.className.includes('font-cruyff-condensed-medium') ||
element.className.includes('font-cruyff-condensed-numbers-medium'))) {
// 标记为已翻译,避免重复翻译
element.dataset.oaoTranslated = 'true';
return;
}
if (element.childNodes.length === 1 && element.firstChild.nodeType === Node.TEXT_NODE) {
const text = element.textContent.trim();
const textUpper = text.toUpperCase();
// 首先尝试原始键查找,然后使用大小写不敏感的sixStatLowerMap
let translation = null;
if (dictionary[text]) {
translation = options.useShortenTerms ? dictionary[text].shorten : dictionary[text].webpagedata;
} else if (dictionary[textUpper]) {
translation = options.useShortenTerms ? dictionary[textUpper].shorten : dictionary[textUpper].webpagedata;
} else if (dictionaryCache.sixStatLowerMap && dictionaryCache.sixStatLowerMap[textUpper]) {
translation = options.useShortenTerms ? dictionaryCache.sixStatLowerMap[textUpper].shorten : dictionaryCache.sixStatLowerMap[textUpper].webpagedata;
}
if (translation) {
element.textContent = options.showOriginalWithBrackets ? `${translation} (${text})` : translation;
// 标记为已翻译,避免重复翻译
element.dataset.oaoTranslated = 'true';
}
}
});
}
};
// 角色翻译器
const roleTranslator = {
translate: function(root, dictionary, options) {
if (!dictionary) return;
// 优化选择器,只选择可能包含文本的元素
const elements = root.querySelectorAll('div, span, p, h1, h2, h3, h4, h5, h6, a, li, button, label, th, td');
elements.forEach(element => {
// 跳过已翻译的元素,避免重复翻译
if (element.dataset.oaoTranslated === 'true') {
return;
}
// 跳过侧边栏按钮,避免翻译按钮文字
if (element.classList.contains('oaevo-sidebar-btn')) {
return;
}
// 跳过翻译管理弹窗内的元素
if (isInTranslationModal(element)) {
return;
}
// 处理单个文本节点的情况
if (element.childNodes.length === 1 && element.firstChild.nodeType === Node.TEXT_NODE) {
const text = element.textContent.trim();
let key = text;
let suffix = '';
// 处理带有 + 符号的角色名称
const plusMatch = text.match(/\+*$/);
if (plusMatch) {
suffix = plusMatch[0];
const baseKey = text.slice(0, -suffix.length);
const baseKeyUpper = baseKey.toUpperCase();
if (dictionary[baseKey] || dictionary[baseKeyUpper]) {
key = dictionary[baseKey] ? baseKey : baseKeyUpper;
}
} else {
const textUpper = text.toUpperCase();
if (dictionary[textUpper] && !dictionary[text]) {
key = textUpper;
}
}
if (dictionary[key]) {
const translation = options.useShortenTerms ? dictionary[key].shorten : dictionary[key].webpagedata;
const finalTranslation = translation + suffix;
element.textContent = options.showOriginalWithBrackets ? `${finalTranslation} (${text})` : finalTranslation;
// 标记为已翻译,避免重复翻译
element.dataset.oaoTranslated = 'true';
}
}
// 处理角色名称和 + 符号在不同元素中的情况
else if (element.tagName === 'A' && element.classList.contains('positive-color')) {
const textNodes = [];
let suffix = '';
// 收集所有文本节点和 + 符号
element.childNodes.forEach(child => {
if (child.nodeType === Node.TEXT_NODE) {
textNodes.push(child.textContent.trim());
} else if (child.tagName === 'DIV') {
const divText = child.textContent.trim();
if (divText.match(/^\++$/)) {
suffix = divText;
}
}
});
const roleName = textNodes.join(' ').trim();
const roleNameUpper = roleName.toUpperCase();
let key = roleName;
if (dictionary[roleNameUpper] && !dictionary[roleName]) {
key = roleNameUpper;
}
if (roleName && dictionary[key]) {
const translation = options.useShortenTerms ? dictionary[key].shorten : dictionary[key].webpagedata;
const finalTranslation = translation + suffix;
// 替换所有文本节点内容
let textIndex = 0;
element.childNodes.forEach(child => {
if (child.nodeType === Node.TEXT_NODE) {
if (textIndex === 0) {
child.textContent = finalTranslation;
} else {
child.textContent = '';
}
textIndex++;
} else if (child.tagName === 'DIV' && child.textContent.trim().match(/^\++$/)) {
child.remove();
}
});
// 标记为已翻译,避免重复翻译
element.dataset.oaoTranslated = 'true';
}
}
// 处理 Fut.gg 页面中的角色名称(第一种结构)
else if (element.classList.contains('text-lightest-gray') && element.classList.contains('font-bold')) {
// 检查是否包含 chemistry 图标元素,如果是则跳过翻译
const chemistryIcon = element.querySelector('.font-chemistry');
if (chemistryIcon) {
return; // 跳过包含 chemistry 图标的角色翻译
}
const spanElement = element.querySelector('span');
if (spanElement) {
// 收集所有文本节点和 + 符号
const textNodes = [];
let suffix = '';
spanElement.childNodes.forEach(child => {
if (child.nodeType === Node.TEXT_NODE) {
textNodes.push(child.textContent.trim());
} else if (child.tagName === 'SPAN' && child.classList.contains('text-green')) {
const greenText = child.textContent.trim();
if (greenText.match(/^\++$/)) {
suffix = greenText;
}
}
});
const roleName = textNodes.join(' ').trim();
if (roleName && dictionary[roleName]) {
const translation = options.useShortenTerms ? dictionary[roleName].shorten : dictionary[roleName].webpagedata;
const finalTranslation = translation + suffix;
// 替换所有文本节点内容
let textIndex = 0;
spanElement.childNodes.forEach(child => {
if (child.nodeType === Node.TEXT_NODE) {
if (textIndex === 0) {
child.textContent = finalTranslation;
} else {
child.textContent = '';
}
textIndex++;
} else if (child.tagName === 'SPAN' && child.classList.contains('text-green') && child.textContent.trim().match(/^\++$/)) {
child.remove();
}
});
// 标记为已翻译,避免重复翻译
element.dataset.oaoTranslated = 'true';
}
}
}
// 处理 Fut.gg 页面中的角色名称(第二种结构)
else if (element.classList.contains('text-sm') && element.classList.contains('text-lightest-gray') && element.classList.contains('font-bold')) {
// 收集所有文本节点和 + 符号
const textNodes = [];
let suffix = '';
element.childNodes.forEach(child => {
if (child.nodeType === Node.TEXT_NODE) {
textNodes.push(child.textContent.trim());
} else if (child.tagName === 'SPAN' && child.classList.contains('text-green')) {
const greenText = child.textContent.trim();
if (greenText.match(/^\++$/)) {
suffix = greenText;
}
}
});
const roleName = textNodes.join(' ').trim();
const roleNameUpper = roleName.toUpperCase();
let key = roleName;
if (dictionary[roleNameUpper] && !dictionary[roleName]) {
key = roleNameUpper;
}
if (roleName && dictionary[key]) {
const translation = options.useShortenTerms ? dictionary[key].shorten : dictionary[key].webpagedata;
const finalTranslation = translation + suffix;
// 替换所有文本节点内容
let textIndex = 0;
element.childNodes.forEach(child => {
if (child.nodeType === Node.TEXT_NODE) {
if (textIndex === 0) {
child.textContent = finalTranslation;
} else {
child.textContent = '';
}
textIndex++;
} else if (child.tagName === 'SPAN' && child.classList.contains('text-green') && child.textContent.trim().match(/^\++$/)) {
child.remove();
}
});
// 标记为已翻译,避免重复翻译
element.dataset.oaoTranslated = 'true';
}
}
});
}
};
// 阵容翻译器
const squadTranslator = {
translate: function(root, dictionary, options) {
if (!dictionary) return;
// 优化选择器,只选择可能包含文本的元素
const elements = root.querySelectorAll('div, span, p, h1, h2, h3, h4, h5, h6, a, li, button, label, th, td');
elements.forEach(element => {
// 跳过已翻译的元素,避免重复翻译
if (element.dataset.oaoTranslated === 'true') {
return;
}
// 跳过侧边栏按钮,避免翻译按钮文字
if (element.classList.contains('oaevo-sidebar-btn')) {
return;
}
// 跳过翻译管理弹窗内的元素
if (isInTranslationModal(element)) {
return;
}
if (element.childNodes.length === 1 && element.firstChild.nodeType === Node.TEXT_NODE) {
const text = element.textContent.trim();
const textUpper = text.toUpperCase();
if (dictionary[text] || dictionary[textUpper]) {
const key = dictionary[text] ? text : textUpper;
const translation = options.useShortenTerms ? dictionary[key].shorten : dictionary[key].webpagedata;
element.textContent = options.showOriginalWithBrackets ? `${translation} (${text})` : translation;
// 标记为已翻译,避免重复翻译
element.dataset.oaoTranslated = 'true';
}
}
});
}
};
// 化学反应翻译器
const chemistryTranslator = {
translate: function(root, dictionary, options) {
if (!dictionary) return;
// 优化选择器,只选择可能包含文本的元素
const elements = root.querySelectorAll('div, span, p, h1, h2, h3, h4, h5, h6, a, li, button, label, th, td');
elements.forEach(element => {
// 跳过已翻译的元素,避免重复翻译
if (element.dataset.oaoTranslated === 'true') {
return;
}
// 跳过侧边栏按钮,避免翻译按钮文字
if (element.classList.contains('oaevo-sidebar-btn')) {
return;
}
// 跳过翻译管理弹窗内的元素
if (isInTranslationModal(element)) {
return;
}
// 处理单个文本节点的情况
if (element.childNodes.length === 1 && element.firstChild.nodeType === Node.TEXT_NODE) {
const text = element.textContent.trim();
const textUpper = text.toUpperCase();
if (dictionary[text] || dictionary[textUpper]) {
const key = dictionary[text] ? text : textUpper;
const translation = options.useShortenTerms ? dictionary[key].shorten : dictionary[key].webpagedata;
element.textContent = options.showOriginalWithBrackets ? `${translation} (${text})` : translation;
// 标记为已翻译,避免重复翻译
element.dataset.oaoTranslated = 'true';
}
}
// 处理带有图标的化学反应风格按钮
else if (element.classList.contains('chem-style-button')) {
element.childNodes.forEach(child => {
if (child.nodeType === Node.TEXT_NODE) {
const text = child.textContent.trim();
if (text && dictionary[text]) {
const translation = options.useShortenTerms ? dictionary[text].shorten : dictionary[text].webpagedata;
child.textContent = options.showOriginalWithBrackets ? `${translation} (${text})` : translation;
}
}
});
// 标记为已翻译,避免重复翻译
element.dataset.oaoTranslated = 'true';
}
// 处理社区化学反应风格按钮
else if (element.classList.contains('community-chem-style')) {
const boldDiv = element.querySelector('.xxs-row.align-center.bold');
if (boldDiv) {
boldDiv.childNodes.forEach(child => {
if (child.nodeType === Node.TEXT_NODE) {
const text = child.textContent.trim();
if (text && dictionary[text]) {
const translation = options.useShortenTerms ? dictionary[text].shorten : dictionary[text].webpagedata;
child.textContent = options.showOriginalWithBrackets ? `${translation} (${text})` : translation;
}
}
});
// 标记为已翻译,避免重复翻译
element.dataset.oaoTranslated = 'true';
}
}
});
}
};
// 风格翻译器
const playstylesTranslator = {
translate: function(root, dictionary, options) {
if (!dictionary) return;
// 优化选择器,只选择可能包含文本的元素
const elements = root.querySelectorAll('div, span, p, h1, h2, h3, h4, h5, h6, a, li, button, label, th, td');
elements.forEach(element => {
// 跳过已翻译的元素,避免重复翻译
if (element.dataset.oaoTranslated === 'true') {
return;
}
// 跳过侧边栏按钮,避免翻译按钮文字
if (element.classList.contains('oaevo-sidebar-btn')) {
return;
}
// 跳过翻译管理弹窗内的元素
if (isInTranslationModal(element)) {
return;
}
// 处理单个文本节点的情况
if (element.childNodes.length === 1 && element.firstChild.nodeType === Node.TEXT_NODE) {
const text = element.textContent.trim();
const textUpper = text.toUpperCase();
if (dictionary[text] || dictionary[textUpper]) {
const key = dictionary[text] ? text : textUpper;
const translation = options.useShortenTerms ? dictionary[key].shorten : dictionary[key].webpagedata;
element.textContent = options.showOriginalWithBrackets ? `${translation} (${text})` : translation;
// 标记为已翻译,避免重复翻译
element.dataset.oaoTranslated = 'true';
}
}
// 处理带有 evo-cap-line 子元素的情况(如 Finesse Shot | 8)
else if (element.querySelector('.evo-cap-line')) {
const firstChild = element.firstChild;
if (firstChild && firstChild.nodeType === Node.TEXT_NODE) {
const text = firstChild.textContent.trim();
const textUpper = text.toUpperCase();
if (dictionary[text] || dictionary[textUpper]) {
const key = dictionary[text] ? text : textUpper;
const translation = options.useShortenTerms ? dictionary[key].shorten : dictionary[key].webpagedata;
firstChild.textContent = options.showOriginalWithBrackets ? `${translation} (${text})` : translation;
// 标记为已翻译,避免重复翻译
element.dataset.oaoTranslated = 'true';
}
}
}
});
}
};
// 进化翻译器
const evolutionsTranslator = {
translate: function(root, dictionary, options) {
if (!dictionary) return;
// 检查是否是Futbin页面
const isFutbinPage = window.location.hostname.includes('futbin.com');
// 检查是否是 futbin player/evolution 页面
const isFutbinPlayerEvolutionPage = isFutbinPage && window.location.href.includes('/player/') && window.location.href.includes('/evolution');
// 如果是 futbin player/evolution 页面,跳过处理,避免破坏页面结构
if (isFutbinPlayerEvolutionPage) {
return;
}
const elements = root.querySelectorAll('*');
elements.forEach(element => {
// 跳过已翻译的元素
if (element.dataset.oaoTranslated === 'true') {
return;
}
// 跳过侧边栏按钮,避免翻译按钮文字
if (element.classList.contains('oaevo-sidebar-btn')) {
return;
}
// 跳过翻译管理弹窗内的元素
if (isInTranslationModal(element)) {
return;
}
// 跳过包含复杂子结构的元素(如包含链接、图片等的卡片)
// 只处理纯文本节点或简单的文本包装元素
const hasComplexChildren = element.querySelector('a, img, svg, button, input, .fc-card-container');
if (hasComplexChildren) {
// 对于包含复杂子元素的元素,只遍历其直接文本节点
let hasTranslated = false;
element.childNodes.forEach(child => {
if (child.nodeType === Node.TEXT_NODE) {
const text = child.textContent.trim();
if (text) {
const normalizedText = text.replace(/&/g, '&');
const normalizedTextUpper = normalizedText.toUpperCase();
if (dictionary[normalizedText] || dictionary[normalizedTextUpper]) {
const key = dictionary[normalizedText] ? normalizedText : normalizedTextUpper;
const translation = options.useShortenTerms ? dictionary[key].shorten : dictionary[key].webpagedata;
// 根据进化格式设置决定显示方式
switch (evolutionFormat) {
case 0: // 中文(英文)
child.textContent = `${translation} (${normalizedText})`;
break;
case 1: // 中文
child.textContent = translation;
break;
case 2: // 英文
child.textContent = normalizedText;
break;
}
hasTranslated = true;
}
// 处理带后缀的进化名称,如 "Dribble Mastery - Season 5 Lvl 8"
else if (normalizedText.includes(' - Season')) {
const baseName = normalizedText.split(' - Season')[0].trim();
const baseNameUpper = baseName.toUpperCase();
if (dictionary[baseName] || dictionary[baseNameUpper]) {
const key = dictionary[baseName] ? baseName : baseNameUpper;
const translation = options.useShortenTerms ? dictionary[key].shorten : dictionary[key].webpagedata;
const suffix = normalizedText.substring(normalizedText.indexOf(' - Season'));
// 根据进化格式设置决定显示方式
switch (evolutionFormat) {
case 0: // 中文(英文)
child.textContent = `${translation} (${baseName})${suffix}`;
break;
case 1: // 中文
child.textContent = translation + suffix;
break;
case 2: // 英文
child.textContent = normalizedText;
break;
}
hasTranslated = true;
}
}
}
}
// 在Futbin页面,处理子元素,确保它们也被正确翻译
else if (isFutbinPage && child.nodeType === Node.ELEMENT_NODE) {
const childText = child.textContent.trim();
if (childText) {
const normalizedChildText = childText.replace(/&/g, '&');
const normalizedChildTextUpper = normalizedChildText.toUpperCase();
if (dictionary[normalizedChildText] || dictionary[normalizedChildTextUpper]) {
const key = dictionary[normalizedChildText] ? normalizedChildText : normalizedChildTextUpper;
const translation = options.useShortenTerms ? dictionary[key].shorten : dictionary[key].webpagedata;
// 根据进化格式设置决定显示方式
switch (evolutionFormat) {
case 0: // 中文(英文)
child.textContent = `${translation} (${normalizedChildText})`;
break;
case 1: // 中文
child.textContent = translation;
break;
case 2: // 英文
child.textContent = normalizedChildText;
break;
}
hasTranslated = true;
}
// 处理带后缀的进化名称,如 "Dribble Mastery - Season 5 Lvl 8"
else if (normalizedChildText.includes(' - Season')) {
const baseName = normalizedChildText.split(' - Season')[0].trim();
const baseNameUpper = baseName.toUpperCase();
if (dictionary[baseName] || dictionary[baseNameUpper]) {
const key = dictionary[baseName] ? baseName : baseNameUpper;
const translation = options.useShortenTerms ? dictionary[key].shorten : dictionary[key].webpagedata;
const suffix = normalizedChildText.substring(normalizedChildText.indexOf(' - Season'));
// 根据进化格式设置决定显示方式
switch (evolutionFormat) {
case 0: // 中文(英文)
child.textContent = `${translation} (${baseName})${suffix}`;
break;
case 1: // 中文
child.textContent = translation + suffix;
break;
case 2: // 英文
child.textContent = normalizedChildText;
break;
}
hasTranslated = true;
}
}
// 处理子元素的文本节点
else {
child.childNodes.forEach(grandChild => {
if (grandChild.nodeType === Node.TEXT_NODE) {
const grandChildText = grandChild.textContent.trim();
if (grandChildText) {
const normalizedGrandChildText = grandChildText.replace(/&/g, '&');
const normalizedGrandChildTextUpper = normalizedGrandChildText.toUpperCase();
if (dictionary[normalizedGrandChildText] || dictionary[normalizedGrandChildTextUpper]) {
const key = dictionary[normalizedGrandChildText] ? normalizedGrandChildText : normalizedGrandChildTextUpper;
const translation = options.useShortenTerms ? dictionary[key].shorten : dictionary[key].webpagedata;
// 根据进化格式设置决定显示方式
switch (evolutionFormat) {
case 0: // 中文(英文)
grandChild.textContent = `${translation} (${normalizedGrandChildText})`;
break;
case 1: // 中文
grandChild.textContent = translation;
break;
case 2: // 英文
grandChild.textContent = normalizedGrandChildText;
break;
}
hasTranslated = true;
}
}
}
});
}
}
// 标记子元素为已翻译,避免重复处理
child.dataset.oaoTranslated = 'true';
}
});
if (hasTranslated) {
element.dataset.oaoTranslated = 'true';
}
return;
}
// 尝试直接文本节点匹配(元素只有一个文本子节点)
if (element.childNodes.length === 1 && element.firstChild.nodeType === Node.TEXT_NODE) {
const text = element.textContent.trim();
// 处理HTML实体,将&转换为&
const normalizedText = text.replace(/&/g, '&');
const normalizedTextUpper = normalizedText.toUpperCase();
if (dictionary[normalizedText] || dictionary[normalizedTextUpper]) {
const key = dictionary[normalizedText] ? normalizedText : normalizedTextUpper;
const translation = options.useShortenTerms ? dictionary[key].shorten : dictionary[key].webpagedata;
// 根据进化格式设置决定显示方式
switch (evolutionFormat) {
case 0: // 中文(英文)
element.textContent = `${translation} (${normalizedText})`;
break;
case 1: // 中文
element.textContent = translation;
break;
case 2: // 英文
element.textContent = normalizedText;
break;
}
element.dataset.oaoTranslated = 'true';
}
// 处理带后缀的进化名称,如 "Dribble Mastery - Season 5 Lvl 8"
else if (normalizedText.includes(' - Season')) {
const baseName = normalizedText.split(' - Season')[0].trim();
if (dictionary[baseName]) {
const translation = options.useShortenTerms ? dictionary[baseName].shorten : dictionary[baseName].webpagedata;
const suffix = normalizedText.substring(normalizedText.indexOf(' - Season'));
// 根据进化格式设置决定显示方式
switch (evolutionFormat) {
case 0: // 中文(英文)
element.textContent = `${translation} (${baseName})${suffix}`;
break;
case 1: // 中文
element.textContent = translation + suffix;
break;
case 2: // 英文
element.textContent = normalizedText;
break;
}
element.dataset.oaoTranslated = 'true';
}
}
}
// 尝试提取完整文本内容匹配(处理复杂DOM结构)
else {
const fullText = element.textContent.trim();
if (fullText) {
// 处理HTML实体,将&转换为&
const normalizedFullText = fullText.replace(/&/g, '&');
// 检查完整文本是否在字典中
const normalizedFullTextUpper = normalizedFullText.toUpperCase();
if (dictionary[normalizedFullText] || dictionary[normalizedFullTextUpper]) {
const key = dictionary[normalizedFullText] ? normalizedFullText : normalizedFullTextUpper;
const translation = options.useShortenTerms ? dictionary[key].shorten : dictionary[key].webpagedata;
// 替换所有文本节点内容
let firstTextNodeFound = false;
element.childNodes.forEach(child => {
if (child.nodeType === Node.TEXT_NODE) {
if (!firstTextNodeFound) {
// 根据进化格式设置决定显示方式
switch (evolutionFormat) {
case 0: // 中文(英文)
child.textContent = `${translation} (${normalizedFullText})`;
break;
case 1: // 中文
child.textContent = translation;
break;
case 2: // 英文
child.textContent = normalizedFullText;
break;
}
firstTextNodeFound = true;
} else {
child.textContent = '';
}
}
// 在Futbin页面,清空子元素内容,避免重复翻译
else if (isFutbinPage && child.nodeType === Node.ELEMENT_NODE) {
child.textContent = '';
child.dataset.oaoTranslated = 'true';
}
});
element.dataset.oaoTranslated = 'true';
}
// 检查子元素的文本内容是否在字典中(只处理纯文本子元素,避免破坏HTML结构)
else {
let hasTranslated = false;
element.childNodes.forEach(child => {
// 处理文本节点
if (child.nodeType === Node.TEXT_NODE) {
const childText = child.textContent.trim();
if (childText) {
// 处理HTML实体,将&转换为&
const normalizedChildText = childText.replace(/&/g, '&');
const normalizedChildTextUpper = normalizedChildText.toUpperCase();
if (dictionary[normalizedChildText] || dictionary[normalizedChildTextUpper]) {
const key = dictionary[normalizedChildText] ? normalizedChildText : normalizedChildTextUpper;
const translation = options.useShortenTerms ? dictionary[key].shorten : dictionary[key].webpagedata;
// 根据进化格式设置决定显示方式
switch (evolutionFormat) {
case 0: // 中文(英文)
child.textContent = `${translation} (${normalizedChildText})`;
break;
case 1: // 中文
child.textContent = translation;
break;
case 2: // 英文
child.textContent = normalizedChildText;
break;
}
hasTranslated = true;
}
// 处理带后缀的进化名称,如 "Dribble Mastery - Season 5 Lvl 8"
else if (normalizedChildText.includes(' - Season')) {
const baseName = normalizedChildText.split(' - Season')[0].trim();
if (dictionary[baseName]) {
const translation = options.useShortenTerms ? dictionary[baseName].shorten : dictionary[baseName].webpagedata;
const suffix = normalizedChildText.substring(normalizedChildText.indexOf(' - Season'));
// 根据进化格式设置决定显示方式
switch (evolutionFormat) {
case 0: // 中文(英文)
child.textContent = `${translation} (${baseName})${suffix}`;
break;
case 1: // 中文
child.textContent = translation + suffix;
break;
case 2: // 英文
child.textContent = normalizedChildText;
break;
}
hasTranslated = true;
}
}
}
}
// 只处理元素节点,且该元素只包含纯文本(没有子元素)
else if (child.nodeType === Node.ELEMENT_NODE && child.children.length === 0) {
const childText = child.textContent.trim();
// 处理HTML实体,将&转换为&
const normalizedChildText = childText.replace(/&/g, '&');
const normalizedChildTextUpper = normalizedChildText.toUpperCase();
if (normalizedChildText && (dictionary[normalizedChildText] || dictionary[normalizedChildTextUpper])) {
const key = dictionary[normalizedChildText] ? normalizedChildText : normalizedChildTextUpper;
const translation = options.useShortenTerms ? dictionary[key].shorten : dictionary[key].webpagedata;
// 根据进化格式设置决定显示方式
switch (evolutionFormat) {
case 0: // 中文(英文)
child.textContent = `${translation} (${normalizedChildText})`;
break;
case 1: // 中文
child.textContent = translation;
break;
case 2: // 英文
child.textContent = normalizedChildText;
break;
}
hasTranslated = true;
}
// 处理带后缀的进化名称,如 "Dribble Mastery - Season 5 Lvl 8"
else if (normalizedChildText && normalizedChildText.includes(' - Season')) {
const baseName = normalizedChildText.split(' - Season')[0].trim();
if (dictionary[baseName]) {
const translation = options.useShortenTerms ? dictionary[baseName].shorten : dictionary[baseName].webpagedata;
const suffix = normalizedChildText.substring(normalizedChildText.indexOf(' - Season'));
// 根据进化格式设置决定显示方式
switch (evolutionFormat) {
case 0: // 中文(英文)
child.textContent = `${translation} (${baseName})${suffix}`;
break;
case 1: // 中文
child.textContent = translation + suffix;
break;
case 2: // 英文
child.textContent = normalizedChildText;
break;
}
hasTranslated = true;
}
}
}
// 在Futbin页面,处理其他子元素
else if (isFutbinPage && child.nodeType === Node.ELEMENT_NODE) {
const childText = child.textContent.trim();
if (childText) {
const normalizedChildText = childText.replace(/&/g, '&');
const normalizedChildTextUpper = normalizedChildText.toUpperCase();
if (dictionary[normalizedChildText] || dictionary[normalizedChildTextUpper]) {
const key = dictionary[normalizedChildText] ? normalizedChildText : normalizedChildTextUpper;
const childTranslation = options.useShortenTerms ? dictionary[key].shorten : dictionary[key].webpagedata;
// 根据进化格式设置决定显示方式
switch (evolutionFormat) {
case 0: // 中文(英文)
child.textContent = `${childTranslation} (${normalizedChildText})`;
break;
case 1: // 中文
child.textContent = childTranslation;
break;
case 2: // 英文
child.textContent = normalizedChildText;
break;
}
hasTranslated = true;
}
// 处理子元素的文本节点
else {
let grandChildTranslated = false;
child.childNodes.forEach(grandChild => {
if (grandChild.nodeType === Node.TEXT_NODE) {
const grandChildText = grandChild.textContent.trim();
if (grandChildText) {
const normalizedGrandChildText = grandChildText.replace(/&/g, '&');
const normalizedGrandChildTextUpper = normalizedGrandChildText.toUpperCase();
if (dictionary[normalizedGrandChildText] || dictionary[normalizedGrandChildTextUpper]) {
const key = dictionary[normalizedGrandChildText] ? normalizedGrandChildText : normalizedGrandChildTextUpper;
const grandChildTranslation = options.useShortenTerms ? dictionary[key].shorten : dictionary[key].webpagedata;
// 根据进化格式设置决定显示方式
switch (evolutionFormat) {
case 0: // 中文(英文)
grandChild.textContent = `${grandChildTranslation} (${normalizedGrandChildText})`;
break;
case 1: // 中文
grandChild.textContent = grandChildTranslation;
break;
case 2: // 英文
grandChild.textContent = normalizedGrandChildText;
break;
}
hasTranslated = true;
grandChildTranslated = true;
}
}
}
});
// 如果孙子节点被翻译了,清空其他孙子节点
if (grandChildTranslated) {
child.childNodes.forEach(grandChild => {
if (grandChild.nodeType === Node.ELEMENT_NODE) {
grandChild.textContent = '';
grandChild.dataset.oaoTranslated = 'true';
}
});
}
}
}
// 标记子元素为已翻译,避免重复处理
child.dataset.oaoTranslated = 'true';
}
});
if (hasTranslated) {
element.dataset.oaoTranslated = 'true';
}
}
}
}
});
},
isTargetPage: function() {
return window.location.href.includes('evolutions') || window.location.href.includes('/evolution');
}
};
// Fut.gg进化翻译器
const futggEvolutionsTranslator = {
translate: function(root, dictionary, options) {
if (!dictionary) return;
// 检查是否是进化页面
const isFutggEvolutionsPage =
window.location.hostname.includes('fut.gg') &&
(window.location.href.includes('evo-lab/evolutions') ||
window.location.href.includes('/evolutions') ||
window.location.href.includes('evo-lab/evolve'));
// 优化选择器,只选择可能包含文本的元素
const elements = root.querySelectorAll('div, span, p, h1, h2, h3, h4, h5, h6, a, li, button, label');
elements.forEach(element => {
// 跳过已翻译的元素
if (element.dataset.oaoTranslated === 'true') {
return;
}
// 跳过侧边栏按钮,避免翻译按钮文字
if (element.classList.contains('oaevo-sidebar-btn')) {
return;
}
// 跳过翻译管理弹窗内的元素
if (isInTranslationModal(element)) {
return;
}
if (element.childNodes.length === 1 && element.firstChild.nodeType === Node.TEXT_NODE) {
const text = element.textContent.trim();
const textUpper = text.toUpperCase();
if (dictionary[text] || dictionary[textUpper]) {
const key = dictionary[text] ? text : textUpper;
const translation = options.useShortenTerms ? dictionary[key].shorten : dictionary[key].webpagedata;
// 根据进化格式设置决定显示方式
switch (evolutionFormat) {
case 0: // 中文(英文)
element.textContent = `${translation} (${text})`;
break;
case 1: // 中文
element.textContent = translation;
break;
case 2: // 英文
element.textContent = text;
break;
}
// 标记为已翻译
element.dataset.oaoTranslated = 'true';
}
}
});
},
isTargetPage: function() {
return window.location.href.includes('evo-lab');
}
};
// Fut.gg翻译器
const futggTranslator = {
translate: function(root, dictionary, options) {
if (!dictionary) return;
// 合并所有字典
const allDictionaries = [dictionary];
if (options && options.extraDictionaries) {
allDictionaries.push(...options.extraDictionaries);
}
// 检查是否是 evo-lab/evolutions 页面
const isEvolutionsPage = window.location.href.includes('evo-lab/evolutions');
const isFutggEvolutionsPage = window.location.href === 'https://www.fut.gg/evolutions/' ||
window.location.href.includes('fut.gg/evolutions');
const isFutggEvolvePage = window.location.href.includes('/evo-lab/evolve');
const isFutggEvolutionsLabPage = window.location.href.includes('/evo-lab/evolutions');
const isFutggPlayerEvolutionsPage = window.location.href.includes('/players/') && window.location.href.includes('/evolutions/');
// 针对 futgg evolutions 页面的特定 HTML 格式进行特殊处理
// 该格式为:
包含
文本
// 只处理尚未处理的元素
if (isFutggEvolutionsPage) {
const evoFilterWrappers = root.querySelectorAll('.bg-gray-900.rounded');
evoFilterWrappers.forEach(wrapper => {
const h2 = wrapper.querySelector('h2.font-bold');
if (h2 && !h2.dataset.oaoFutggEvoProcessed) {
let text = h2.textContent.trim();
if (text) {
// 匹配后缀模式 - 支持各种括号内的内容
const suffixMatch = text.match(/\s*\[[^\]]+\]$/);
let baseText = text;
let suffix = '';
if (suffixMatch) {
baseText = text.replace(suffixMatch[0], '').trim();
suffix = suffixMatch[0];
}
const baseTextUpper = baseText.toUpperCase();
let translated = false;
// 首先在进化字典中查找
if (options.evolutionDictionary) {
if (options.evolutionDictionary[baseText] || options.evolutionDictionary[baseTextUpper]) {
const key = options.evolutionDictionary[baseText] ? baseText : baseTextUpper;
const dictValue = options.evolutionDictionary[key];
const translation = typeof dictValue === 'object'
? (options.useShortenTerms ? dictValue.shorten : dictValue.webpagedata)
: dictValue;
// 按照进化格式显示
switch (evolutionFormat) {
case 0: // 中文(英文)
h2.textContent = `${translation} (${key})${suffix}`;
break;
case 1: // 中文
h2.textContent = translation + suffix;
break;
case 2: // 英文 - 保持原始英文,不翻译
h2.textContent = key + suffix;
break;
}
translated = true;
}
}
// 如果进化字典中没找到,在所有字典中查找
if (!translated) {
for (const dict of allDictionaries) {
// 正向查找:baseText 作为 key
if (dict[baseText] || dict[baseTextUpper]) {
const key = dict[baseText] ? baseText : baseTextUpper;
const dictValue = dict[key];
const translation = typeof dictValue === 'object'
? (options.useShortenTerms ? dictValue.shorten : dictValue.webpagedata)
: dictValue;
// 按照进化格式显示
switch (evolutionFormat) {
case 0: // 中文(英文)
h2.textContent = `${translation} (${key})${suffix}`;
break;
case 1: // 中文
h2.textContent = translation + suffix;
break;
case 2: // 英文 - 保持原始英文,不翻译
h2.textContent = key + suffix;
break;
}
translated = true;
break;
}
// 反向查找:当元素显示中文时,尝试找到对应的原始英文 key
// 在 evolutionFormat === 0 或 2 时,如果当前是中文,需要恢复英文
if (!translated && (evolutionFormat === 0 || evolutionFormat === 2)) {
for (const key in dict) {
if (dict.hasOwnProperty(key)) {
const dictValue = dict[key];
const translation = typeof dictValue === 'object'
? (options.useShortenTerms ? dictValue.shorten : dictValue.webpagedata)
: dictValue;
if (translation === baseText) {
// 找到了匹配的翻译,按照格式显示
switch (evolutionFormat) {
case 0: // 中文(英文)
h2.textContent = `${baseText} (${key})${suffix}`;
break;
case 2: // 英文 - 恢复原始英文
h2.textContent = key + suffix;
break;
}
translated = true;
break;
}
}
}
}
if (translated) break;
}
}
// 如果还是没找到翻译且是英文格式,保持原样
if (!translated && evolutionFormat === 2) {
// 保持原样,不翻译
}
}
// 标记为已处理,避免重复处理
h2.dataset.oaoFutggEvoProcessed = 'true';
// 同时标记为已翻译,避免主翻译循环再次处理
h2.dataset.oaoTranslated = 'true';
}
});
}
// 针对 futgg evo-lab/evolve 页面的特定 HTML 格式进行特殊处理
// 该格式为: