您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
为赛马娘官网内容提供中文翻译。
// ==UserScript== // @name 赛马娘官网中文翻译 // @namespace http://umamusume.jp/ // @version 1.1.0 // @description 为赛马娘官网内容提供中文翻译。 // @author yingyingyingqwq // @match *://umamusume.jp/* // @icon https://umamusume.jp/favicon.ico // @grant GM_getResourceText // @resource Replacements https://umasite.yingqwq.cn/Replacements.json // ==/UserScript== (function () { 'use strict'; const Replacements = JSON.parse(GM_getResourceText("Replacements")); const IMGreplace = Replacements[3].images function replaceImageSource() { const path = window.location.pathname; if (IMGreplace[path]) { const imgConfig = IMGreplace[path]; for (const altValue in imgConfig) { const images = document.querySelectorAll(`img[alt="${altValue}"]`); const replacements = imgConfig[altValue]; images.forEach((img, index) => { if (replacements[index + 1]) { img.src = replacements[index + 1]; } }); } } } function escapeRegExp(string) { return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } // 预编译所有替换规则 const compiledReplacements = Replacements.map(replacement => { const partialPatterns = Object.keys(replacement.partial || {}).flatMap(key => { if (replacement.type === 'url' || replacement.type === 'element') { return Object.keys(replacement.partial[key]).map(subKey => ({ regex: new RegExp(escapeRegExp(subKey), 'g'), value: replacement.partial[key][subKey], matchType: "partial", context: key })); } else { return { regex: new RegExp(escapeRegExp(key), 'g'), value: replacement.partial[key], matchType: "partial" }; } }); const fullPatterns = Object.keys(replacement.full || {}).flatMap(key => { if (replacement.type === 'url' || replacement.type === 'element') { return Object.keys(replacement.full[key]).map(subKey => ({ regex: new RegExp(escapeRegExp(subKey)), value: replacement.full[key][subKey], matchType: "full", context: key })); } else { return { regex: new RegExp(escapeRegExp(key)), value: replacement.full[key], matchType: "full" }; } }); return { ...replacement, patterns: [...partialPatterns, ...fullPatterns] }; }); function getReplacementsForURL() { const path = window.location.pathname; return compiledReplacements.filter(replacement => replacement.type === "global" || (replacement.type === "url" && replacement.patterns.some(pattern => pattern.context === path)) ); } function getReplacementsForElement(element) { const dataAttributes = Array.from(element.attributes).map(attr => attr.name); return compiledReplacements.filter(replacement => replacement.type === "global" || (replacement.type === "element" && replacement.patterns.some(pattern => dataAttributes.includes(pattern.context))) ); } function ReplaceText(node) { const walker = document.createTreeWalker(node, NodeFilter.SHOW_TEXT, null, false); let currentNode = walker.nextNode(); const urlReplacements = getReplacementsForURL(); while (currentNode) { const elementReplacements = getReplacementsForElement(currentNode.parentElement); const allReplacements = [...urlReplacements, ...elementReplacements]; let text = currentNode.nodeValue; allReplacements.forEach(({ patterns }) => { patterns.forEach(({ regex, value, matchType, context }) => { if (context && window.location.pathname !== context && !currentNode.parentElement.hasAttribute(context)) return; if (matchType === "partial") { text = text.replace(regex, value); } else if (matchType === "full" && text === regex.source) { text = value; } }); }); if (text !== currentNode.nodeValue) { currentNode.nodeValue = text; } currentNode = walker.nextNode(); } } function ReplaceTitle() { let title = document.title; const urlReplacements = getReplacementsForURL(); urlReplacements.forEach(({ patterns }) => { patterns.forEach(({ regex, value, matchType, context }) => { if (context && window.location.pathname !== context) return; if (matchType === "partial") { title = title.replace(regex, value); } else if (matchType === "full" && title === regex.source) { title = value; } }); }); document.title = title; } function replaceYoutubeVideo() { const videoMapping = Replacements[4].videos const youtubeIframes = document.querySelectorAll('iframe[src*="youtube-nocookie.com/embed/"]'); youtubeIframes.forEach(iframe => { const youtubeUrl = new URL(iframe.src); const videoId = youtubeUrl.pathname.split('/').pop(); const bilibiliId = videoMapping[videoId]; if (bilibiliId) { const bilibiliUrl = `https://player.bilibili.com/player.html?bvid=${bilibiliId}&high_quality=1`; const bilibiliIframe = document.createElement('iframe'); bilibiliIframe.src = bilibiliUrl; bilibiliIframe.width = iframe.width; bilibiliIframe.height = iframe.height; bilibiliIframe.frameBorder = "0"; bilibiliIframe.allow = "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"; bilibiliIframe.allowFullscreen = true; iframe.parentNode.replaceChild(bilibiliIframe, iframe); } }); } function changeFont() { if (!document.querySelector('#customFontStyle')) { const styleElement = document.createElement('style'); styleElement.id = 'customFontStyle'; document.head.appendChild(styleElement); styleElement.textContent = ` body,label,.character-detail__visual-catch,.font-style-italic,.catch-text { font-family: Misans,YakuHanJP,Roboto,Zen Kaku Gothic New,sans-serif,微软雅黑 !important; } .mainstory-part1-section[data-v-dbd344ea],.font-weight-regular { font-family: Noto Serif SC,Noto Serif JP,serif !important; } `; } } function observeDOMChanges() { const observer = new MutationObserver((mutations) => { ReplaceText(document.body); ReplaceTitle(); replaceImageSource(); replaceYoutubeVideo() }); observer.observe(document.body, { childList: true, subtree: true }); } function loadMiSansFont() { var link = document.createElement('link'); link.rel = 'stylesheet'; link.crossOrigin = 'anonymous'; link.href = 'https://cdn.jsdelivr.net/npm/[email protected]/lib/Normal/MiSans-Medium.min.css'; document.head.appendChild(link); } function loadSongFont() { var link = document.createElement('link'); link.rel = 'stylesheet'; link.crossOrigin = 'anonymous'; link.href = 'https://fonts.font.im/css2?family=Noto+Serif+SC:[email protected]&display=swap'; document.head.appendChild(link); } loadMiSansFont(); loadSongFont(); ReplaceText(document.body); ReplaceTitle(); changeFont(); observeDOMChanges(); replaceImageSource(); replaceYoutubeVideo() })();