// ==UserScript== // @name CurseForge 界面汉化 // @namespace https://github.com/Chuc-Jie/curseforge-chinese // @version 1.3.0 // @description 汉化 CurseForge 网站界面,整词匹配,避免破坏句子。优化性能与动态路由支持。 // @author 友野YouyEr // @icon https://www.curseforge.com/favicon.ico // @match *://*.curseforge.com/* // @match https://www.curseforge.com/* // @grant none // @noframes // @license GPL-3.0 // @run-at document-end // ==/UserScript== (function() { 'use strict'; // ======================= 忽略区域配置 ======================= const ignoredSelectors = [ 'code', 'pre', 'script', 'style', 'textarea', 'kbd', '.CodeMirror', '.monaco-editor', '.cm-editor', '.codemirror-textarea', 'input[type="text"]', 'input[type="password"]', 'input[type="email"]', '[data-do-not-translate]', '[data-translation-ignore]', '.file-list', '.version-table', '.hash-value', '.project-file-list', '.game-version-select', '.mod-detail__hash', '.mod-detail__filename', '.mod-card__version', '.mod-card__game-version', '.mod-detail__file-info', '[data-version]', '[data-file-id]', '.mod-author__name', '.mod-title__name', '.mod-description__content', '.file-row .name', '.project-name', '.game-version', '.version-badge', '.overlay-link' // '.dropdown-list li' 注释掉以让下拉菜单被翻译 ]; // 改用 Set 并精确匹配 class(基于 classList) const ignoredClassesSet = new Set([ 'CodeBlock', 'gitSha', 'monospace', 'file-list', 'version-info', 'mod-author', 'mod-name', 'mod-detail__hash', 'game-version', 'mod-relations', 'mod-issues', 'mod-source', 'mod-version-number', 'project-id', 'file-name', 'version-number' ]); // ======================= 翻译词典 ======================= const i18n = new Map([ // 导航栏 ['Browse','浏览'], ['Create','创作'], ['MOD AUTHORS', '模组作者'], ['Become an Author', '成为作者'], ['Start a Project', '创建项目'], ['Project Submission Guide', '项目提交指南'], ['MODDING TOOL DEVELOPERS', '模组工具开发者'], ['Project submission guide','项目提交指南'], ['Author Rewards Program','作者奖励计划'], ['Apply for an API Key', '申请 API 密钥'], ['3rd Party API T&C','第三方API条款'], ['Documentation', '文档'], ['Terms & Conditions', '条款与规则'], ['SOCIAL', '社交'], ['Join Our Discord!', '加入我们的 Discord!'], ['Authors Discord', '作者 Discord'], ['Hytale New Worlds Contest', 'Hytale 新世界创作大赛'], ['Studios','工作室'], ['GAME DEVELOPERS', '游戏开发者'], ['Unlock Modding for Your Game', '为你的游戏开放模组功能'], ['Documentation', '文档'], ['Hytale New Worlds Contest', 'Hytale 新世界大赛'], ['Go Premium','开通高级版'], ['Get CurseForge App','获取 CurseForge 应用'], ['Help Center','帮助中心'], ['Our Blog','我们的博客'], ['Surprise me','随机推荐'], ['Legacy','旧版站点'], ['Login','登录'], // 底部导航栏 ['Games', '游戏'], ['All games','全部游戏'], ['Minecraft','我的世界'], ['World of Warcraft','魔兽世界'], ['The Sims 4','模拟人生4'], ['Starcraft II','星际争霸2'], ['Kerbal Space Program','坎巴拉太空计划'], ['Minecraft Dungeons','我的世界:地下城'], ['World of Tanks','坦克世界'], ['Create','创作'], ['Start a project','创建项目'], ['Project submission guide','项目提交指南'], ['Author Rewards Program','作者奖励计划'], ['Apply for an API Key','申请API密钥'], ['3rd Party API T&C','第三方API条款'], ['Community','社区'], ['Join Our Discord!','加入我们的Discord!'], ['Ideas Portal','建议反馈'], ['CF blog','CurseForge博客'], ['Newsletter','订阅通讯'], ['Roadmap','发展路线'], ['Bukkit forums','Bukkit论坛'], ['CurseForge Servers','CurseForge服务器'], ['Support','支持'], ['Knowledge base','知识库'], ['Troubleshooting','故障排除'], ['Contact us','联系我们'], ['Companions','合作伙伴'], ['Overwolf','Overwolf'], ['CurseForge for Studios','面向工作室的CurseForge'], ['Tebex','Tebex'], ['Nitro','Nitro'], ['Terms of Service','服务条款'], ['Privacy Policy','隐私政策'], ['Licenses','许可协议'], ['Mod Author Terms','模组作者条款'], // ================================== 分割线 ================================== // 主页 ['Explore Thousands of Legendary Mods','探索数千款传奇模组'], ['Discover endless mods for your favorite games, or forge your own and share them with millions.','发现海量适用于你喜爱游戏的模组,或是亲手打造专属模组,与数百万玩家分享'], ['CurseForge makes modding easy, safe, and rewarding.','CurseForge 让模组创作变得简单、安全且富有成就感'], ['MODS','模组'], ['DOWNLOADS','下载'], ['MOD AUTHORS','模组作者'], ['PAID TO CREATORS','创作者收益'], ['Search for a game...','搜索游戏...'], ['View all games','查看所有游戏'], ['The Easiest Way to Manage Your Mods','管理模组的最简方式'], ['Download the CurseForge app to install, update, and manage your mods in one click.','下载CurseForge应用,一键安装、更新并管理你的所有模组'], ['It’s fast, safe, and built for the games you love – no setup, no stress, just play.','它快速、安全,专为你喜爱的游戏打造——无需繁琐设置,轻松畅玩'], ['One-Click Installs','一键安装'], ['Add mods with a single click – easy installation, no manual setup, no waiting.','一键添加模组,安装简便,无需手动配置,无需漫长等待'], ['Easy Mod Management','轻松管理模组'], ['Clean updates and reliable support for the games you love – Minecraft, The Sims 4, World of Warcraft, and more.','为你喜爱的游戏提供清爽更新与稳定支持,包括《我的世界》《模拟人生4》《魔兽世界》等'], ['Safe & Secure','安全可靠'], ['CurseForge scans and moderates every mod, giving you fast, worry-free installs and total peace of mind.','CurseForge会扫描审核每一个模组,让你安装快速无忧,安心使用'], ['Create, Share, and Earn','创作、分享并赚取收益'], ['Ready to share your creation? Publish on CurseForge, reach millions of players, and earn rewards with one of the industry\'s most trusted built-in monetization programs.','准备好分享你的创作了吗?在CurseForge发布作品,触达数百万玩家,并通过业内最值得信赖的内置变现计划获取奖励'], ['Creation & Management Tools','创作与管理工具'], ['Publish and manage your mods with tools designed specifically for creators.','使用专为创作者打造的工具发布并管理你的模组'], ['Multi-Platform Exposure','多平台曝光'], ['Reach players across PC, Mac, and Linux from one unified platform.','通过统一平台触达PC、Mac及Linux端的玩家'], ['Paid To Creators','创作者收益'], ['Join thousands of authors already earning on CurseForge.','加入数千位已在CurseForge获得收益的作者行列'], ['Integrate UGC Into Your Game','将UGC内容融入你的游戏'], ['CurseForge for Studios helps game studios launch and scale thriving modding ecosystems.','面向游戏工作室的CurseForge,助力搭建并拓展繁荣的模组生态'], ['From discovery to cross-platform delivery, we provide tools to keep UGC secure, curated, and engaging','从内容发现到跨平台分发,我们提供工具保障UGC安全、优质且富有吸引力'], ['so your game stays fresh, valuable, and exciting for players over time.','让你的游戏长久保持新颖、优质,持续为玩家带来乐趣'], ['Safe & Curated','安全精选'], ['A vetted ecosystem that keeps content safe, secure, and compliant.','经过严格审核的生态系统,保障内容安全合规'], ['Cross-Platform Reach','跨平台覆盖'], ['Seamless distribution across PC, console, and mobile.','在PC、主机及移动端无缝分发'], ['Seeding & Scaling','生态启动与拓展'], ['Launch with ready-to-play mods from day one, and expand your ecosystem with scalable monetization options.','上线即拥有可直接体验的模组,并通过可拓展的变现方案壮大生态'], ['Get in Touch with Our Team','联系我们的团队'], ['Stay Connected with the Modding Community','与模组社区保持联动'], ['Follow us for the latest mods, creator highlights, and community updates – stay in the loop wherever you play or create.','关注我们获取最新模组、创作者风采及社区动态,随时随地不错过任何资讯'], ['Explore Updates, Tips & Stories on the CurseForge Blog','在CurseForge博客查看更新、技巧与故事'], ['Discover new features, modding tips, creator stories, and community news – all straight from the source.','了解全新功能、模组制作技巧、创作者故事与社区资讯,一手消息直达'], ['CurseForge Blog','CurseForge博客'], ['Go Ad-Free and Support Creators with','开启无广告体验,支持创作者'], ['CurseForge Premium','CurseForge高级版'], ['Enjoy ad-free browsing, a customizable app, and a way to support your favorite creators – all with CurseForge Premium.','订阅CurseForge高级版,畅享无广告浏览、个性化应用,同时支持你喜爱的创作者'], ['Upgrade your experience today.','立即升级,提升体验'], ['Go Premium','开通高级版'], // ================================== 分割线 ================================== // 浏览 > 我的世界 // 发现 ['Minecraft Mods', '我的世界模组'], ['projects', '个项目'], ['Minecraft Mods on CurseForge - The Home for the Best Minecraft Mods', 'CurseForge上的我的世界模组——优质我的世界模组聚集地'], ['Discover the best Minecraft Mods and Modpacks around.', '发现全网优质的我的世界模组与整合包'], ['Minecraft is an action-adventure sandbox game where players can build pretty much anything they like, explore their surroundings, craft items, and even engage in combat.', '我的世界是一款动作冒险沙盒游戏,玩家可随心建造、探索世界、合成物品,甚至参与战斗'], ['MC has one of the biggest modding communities in the world, and on this very page - you\'ll be able to become a part of it.', '我的世界拥有全球最庞大的模组社区之一,而在这个页面,你就能成为其中一员。'], ['If you\'ve been looking around some Minecraft forums recently, you probably know that this is the home for all the best Minecraft mods.', '如果你最近逛过我的世界论坛,就会知道这里是所有优质我的世界模组的大本营。'], ['Here, you\'ll be able to easily find and download the best Minecraft mods and modpacks around.', '在这里,你可以轻松找到并下载全网最好的我的世界模组和整合包。'], ['From mods that change Minecraft\'s game interface, through mods that optimize its gameplay, or even mods that offer various tools for improved building, combating, or exploration.', '从更改游戏界面的模组,到优化游戏运行的模组,甚至是提供各类工具、让建造、战斗和探索更便捷的模组应有尽有。'], ['Browse through the selection of MC mods and modpacks, check out their descriptions and photos, and find out which ones are best for you.', '浏览精选的我的世界模组与整合包,查看介绍和截图,找到最适合你的那一款。'], ['Always keep in mind that each and every mod is completely free, so you can try them all until you find your favorite Minecraft mods and modpacks.', '请记住,所有模组均完全免费,你可以尽情尝试,直到找到心仪的我的世界模组与整合包。'], ['And of course, the important thing is to have fun with these Minecraft mods - using them to create a personalized game experience that\'s best for you.', '当然,最重要的是体验这些我的世界模组带来的乐趣,用它们打造专属你的个性化游戏体验。'], ['Read more', '查看更多'], ['Discover','发现'], ['Browse All', '浏览全部'], ['+ Projects found', '+ 个项目被找到'], ['Minecraft Mods Community Picks','我的世界模组 社区精选'], ['Discover top Mods from previous Minecraft community picks and explore even more curated content through our archive','查看往期我的世界社区精选热门模组,并通过存档探索更多精选内容'], ['View Collections','查看合集'], ['Monthly Theme - Fantasy','每月主题 - 奇幻'], ['Popular Categories','热门分类'], ['Modpacks','整合包'], ['Shaders', '光影'], ['Mods', '模组'], ['Bukkit Plugins','Bukkit插件'], ['Addons', '插件'], ['Worlds', '地图'], ['Resource Packs', '资源包'], ['Customization', '自定义'], ['From Top Authors','顶尖作者作品'], ['Latest Mods','最新模组'], ['Latest Modpacks','最新整合包'], ['Latest Resource Packs','最新资源包'], ['Latest Worlds','最新存档地图'], ['Popular Resource Packs','热门资源包'], ['Popular Modpacks','热门整合包'], ['Popular Mods','热门模组'], ['Popular Worlds','热门地图'], // 浏览全部 ['All','全部'], ['Filters','筛选'], ['Game Version','游戏版本'], // 排序方式>相关度 ['Relevancy','相关性'], ['Popularity','热门度'], ['Latest update','最近更新'], ['Creation Date','创建日期'], ['Total Downloads','总下载量'], ['A-Z', 'A到Z'], ['Browse by', '按类别浏览'], ['Popular Categories', '热门分类'], ['Browse all', '浏览全部'], // ================================== 分割线 ================================== // 全大写变体(解决大小写敏感) ['DOWNLOAD', '下载'], ['INSTALL', '安装'], ['LOGIN', '登录'], ['PAGE', '页'], // 用于全大写场景 ['CLEAR ALL', '全部清除'], // 原有词条 // ['',''], ['Minecraft Bedrock', '我的世界基岩版'], // 搜索栏 ['Search for Minecraft mods...', '搜索 我的世界 模组...'], // 二级导航栏 ['Description','简介'],['Files','文件(在这里下载)'],['Gallery','图库'],['Relations','关联'],['Issues','问题'], // 右侧悬浮导航 ['Details','详情'], ['Downloads','下载量'],['Created','创建时间'], ['Updated','更新时间'],['Project ID','项目 ID'], ['Game Versions','游戏版本'], ['Mod Loaders','模组加载器'], ['Categories','分类'], ['Game version', '游戏版本'], ['Sort by', '排序方式 '], ['Relevancy', '相关度 '], ['Main File','主文件'], ['Recent Files','最近文件'], ['Server Packs','服务端整合包'], ['View all','查看全部'], ['Show alpha files','显示 alpha 版本文件'], ['Page','页'], ['Show per page','每页显示'], ['All Game Versions','全部游戏版本'], ['All Mod Loaders','全部模组加载器'], ['Type','类型'], ['Name','名称'], ['Size','大小'], ['Game Ver.','游戏版本'], ['Download','下载'], ['Install','安装'], ['Favorite','收藏'], ['Follow','关注'], ['Report','举报'], ['Links','链接'], ['License','许可证'], ['All Rights Reserved','保留所有权利'], // 月份 ['Jan','1月'],['Feb','2月'],['Mar','3月'],['Apr','4月'],['May','5月'],['Jun','6月'], ['Jul','7月'],['Aug','8月'],['Sep','9月'],['Oct','10月'],['Nov','11月'],['Dec','12月'], // 时间 ['months ago','月前'], ['hours ago','小时前'], // ['Team', '团队'], ['Owner', '作者'], ['Followers', '关注者'], ['Projects', '个项目'], ['Downloads','下载量'], ['Donate', '捐赠'], ['Members', '成员'], ['Author', '作者'], ['Contributor', '贡献者'], // ['More from', '更多来自'], // ['CurseForge - a world of endless gaming possibilities for modders and gamers alike.','CurseForge—— 为模组作者与玩家打造,充满无限游戏可能的世界。'], ['Download the best mods and addons!','下载优质模组与扩展插件!'], // 下载页面提示 ['STILL DOWNLOADING MANUALLY??','还在手动下载吗?'], ['Join over 10 million players who use the CurseForge app!','加入超千万使用 CurseForge 客户端的玩家吧!'], ]); // ---------- 预编译正则表达式 ---------- let sortedKeys = []; let compiledRegexes = new Map(); function escapeRegExp(string) { return string.replace(/[.*+?^${}()|[\]\\\-/]/g, '\\$&'); } function getBoundaryRegex(key) { // 整词边界:前后不是字母数字(包括下划线) return new RegExp('(? b.length - a.length); compiledRegexes.clear(); for (const key of sortedKeys) { compiledRegexes.set(key, getBoundaryRegex(key)); } } updateSortedKeys(); // ---------- 忽略节点检查(带缓存,避免重复向上遍历)---------- // 为每个节点添加一个临时属性标记忽略状态,避免重复计算 const ignoreCache = new WeakMap(); function shouldIgnoreNode(node) { if (!node || node.nodeType !== 1) return false; // 检查缓存 if (ignoreCache.has(node)) return ignoreCache.get(node); // 检查自身选择器 if (ignoredSelectors.some(selector => node.matches && node.matches(selector))) { ignoreCache.set(node, true); return true; } // 检查自身类名(精确匹配) if (node.classList) { for (let cls of node.classList) { if (ignoredClassesSet.has(cls)) { ignoreCache.set(node, true); return true; } } } // 向上检查父节点 let parent = node.parentNode; while (parent && parent !== document.body && parent.nodeType === 1) { if (ignoredSelectors.some(selector => parent.matches && parent.matches(selector))) { ignoreCache.set(node, true); return true; } if (parent.classList) { for (let cls of parent.classList) { if (ignoredClassesSet.has(cls)) { ignoreCache.set(node, true); return true; } } } parent = parent.parentNode; } ignoreCache.set(node, false); return false; } // ---------- 核心翻译函数 ---------- function translateTextNode(node) { if (!node || node.nodeType !== 3) return; const original = node.nodeValue; if (!original || original.trim() === '') return; // 避免翻译纯数字 if (/^\d+$/.test(original.trim())) return; let text = original; let translated = false; for (const key of sortedKeys) { const regex = compiledRegexes.get(key); if (regex && regex.test(text)) { const value = i18n.get(key); text = text.replace(regex, value); translated = true; } } if (translated && text !== original) { node.nodeValue = text; } } function translateAttribute(element, attrName) { if (!element || !element.hasAttribute(attrName)) return; const attrValue = element.getAttribute(attrName); if (!attrValue || attrValue.trim() === '') return; if (/^\d+$/.test(attrValue.trim())) return; let newValue = attrValue; let translated = false; for (const key of sortedKeys) { const regex = compiledRegexes.get(key); if (regex && regex.test(newValue)) { newValue = newValue.replace(regex, i18n.get(key)); translated = true; } } if (translated && newValue !== attrValue) { element.setAttribute(attrName, newValue); } } // 翻译一个节点及其后代(增量) function translateNode(node) { if (!node) return; if (node.nodeType === 3) { translateTextNode(node); return; } if (node.nodeType !== 1) return; if (shouldIgnoreNode(node)) return; // 翻译属性 if (node.hasAttribute('title')) translateAttribute(node, 'title'); if (node.hasAttribute('placeholder')) translateAttribute(node, 'placeholder'); if (node.hasAttribute('aria-label')) translateAttribute(node, 'aria-label'); if ((node.tagName === 'INPUT' || node.tagName === 'BUTTON') && node.hasAttribute('value') && node.getAttribute('type') !== 'password') { translateAttribute(node, 'value'); } // 遍历子节点(只遍历直接子节点,避免重复遍历深层已处理节点) const children = node.childNodes; for (let i = 0; i < children.length; i++) { const child = children[i]; if (child.nodeType === 3) { translateTextNode(child); } else if (child.nodeType === 1 && !shouldIgnoreNode(child)) { // 对于元素节点,递归处理(但注意避免过深递归,CurseForge 层级有限) translateNode(child); } } } // 全量翻译(用于初始化、路由变化) function fullTranslate() { translateNode(document.body); } // ---------- 监听动态内容 ---------- let translationTimer = null; const observer = new MutationObserver(mutations => { clearTimeout(translationTimer); translationTimer = setTimeout(() => { for (const mutation of mutations) { // 处理新增节点 for (const node of mutation.addedNodes) { if (node.nodeType === 1) { translateNode(node); } else if (node.nodeType === 3 && node.nodeValue.trim()) { translateTextNode(node); } } // 处理属性变化 if (mutation.type === 'attributes' && ['title', 'placeholder', 'aria-label'].includes(mutation.attributeName)) { translateAttribute(mutation.target, mutation.attributeName); } // 处理文本变化 if (mutation.type === 'characterData' && mutation.target) { translateTextNode(mutation.target); } } }, 80); }); observer.observe(document.body, { childList: true, subtree: true, characterData: true, attributes: true, attributeFilter: ['title', 'placeholder', 'aria-label'] }); // ---------- 监听 SPA 路由变化 ---------- function onUrlChange() { setTimeout(fullTranslate, 200); } const originalPushState = history.pushState; const originalReplaceState = history.replaceState; history.pushState = function(...args) { originalPushState.apply(this, args); onUrlChange(); }; history.replaceState = function(...args) { originalReplaceState.apply(this, args); onUrlChange(); }; window.addEventListener('popstate', onUrlChange); window.addEventListener('hashchange', onUrlChange); // 点击处理:针对动态弹窗(某些弹窗不触发 MutationObserver 的 addedNodes,只改变 display) document.addEventListener('click', (e) => { setTimeout(() => { const target = e.target; if (target && (target.closest('button') || target.closest('[role="button"]') || target.closest('a'))) { // 延迟等待弹窗渲染 setTimeout(() => { document.querySelectorAll('.modal, .dropdown-menu, .popover, .dialog').forEach(el => { if (el.style.display !== 'none') translateNode(el); }); }, 150); } }, 100); }, true); // ---------- 初始化 ---------- if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => { fullTranslate(); }); } else { fullTranslate(); } window.addEventListener('load', () => { // 确保所有资源加载后再次翻译(捕获懒加载内容) setTimeout(fullTranslate, 300); }); // 脚本卸载时断开观察器(可选) window.addEventListener('beforeunload', () => { observer.disconnect(); }); })();