Royal MCP 插件汉化去广告:把 WordPress 插件改成自己的,官方更新再也覆盖不掉

Zou, Ning 发布于 19 小时前 技术实践 3636 字 30 次阅读


Royal MCP 是一个 WordPress.org 上的开源插件(GPLv2),作用是把站点变成一个 MCP 服务器,让 Claude、ChatGPT 这类客户端能读写文章、设置和媒体。我用它接自己的博客,用了一阵子后动手做了三件事:删掉后台的推广位,把 741 条界面字符串译成中文,再让官方包再也覆盖不掉这些改动。

三件里最费劲的不是翻译,也不是删广告,而是最后一件。机制是这样的:只要 WordPress.org 上存在同名 slug 的条目,后台点一次「更新」,插件目录就被整包替换,改动全部消失,而且不会给任何提示。更麻烦的是,我加的防护措施和被防护的代码写在同一份文件里,真出事时是一起没的。

先量清楚,再决定动哪里

动手之前先把整个包拉到本地数了一遍。140 个文件、3,188 KB、53,773 行 PHP。核心是 Server.php 一个文件 12,357 行,105 个 wp_* 工具全部内联在里面,另有 29 个集成适配器再贡献 157 个工具。真正想删的推广集中在 includes/chrome/ 和 Admin/Pro_Notice.php,共 9 处投放点、1,800 行、74 KB,外加 102 KB 图片。

模块规模性质
includes/MCP/Server.php12,357 行核心,105 个工具内联
includes/Integrations/19,452 行 / 861 KB29 个适配器,157 个工具
includes/Admin/9 个后台页设置、日志、客户端、洞察
includes/chrome/ + Pro_Notice1,800 行 / 74 KB推广层,可整块摘
诊断类通知1,848 行主机排障提示,不能删

顺带一个反直觉的数字:诊断类通知比广告还大,光 Well_Known_Notice.php 一个文件就 1,567 行。内容全是各种主机环境下的排障提示——Apache 丢 Authorization 头、Sucuri 边缘拦 .well-known、Imunify360 挡发现路径、朴素固定链接导致 OAuth 路由失效。这些不是广告,删掉等于把排障能力一起删了,所以留着。

让对面闭嘴,判定不在你的站上

这一点很多人搞反了,以为要在自己站上加个过滤器去拦更新。实际流程是:wp_update_plugins() 把全部插件的头部信息(含 Update URI 字段)打包 POST 到 api.wordpress.org,由对方的服务端按 slug 匹配版本、决定答不答。你的站只负责问,答不答是对方的事。

所以「挡住更新」不是本地拦截,而是让对面看不见这个插件。为了确认这一点,我在服务器上做了一组控制变量实验:同一份 royal-mcp 条目,只改 Update URI 一个字段,其余字段完全一致,逐个回放。

Update URI 取值接口返回
不设 / 空字符串返回更新 → 1.5.12
false响应里完全没有该条目
第三方域名响应里完全没有该条目
https://wordpress.org/plugins/royal-mcp/返回更新 → 1.5.12

结论很清楚:只要有值、且 host 不是 wordpress.org,对面就直接不返回这个条目。顺带测出一个现实风险——插件目录接口报最新版是 1.5.13,而更新检查接口还停在 1.5.12,两个接口的缓存不同步。也就是说站上的 1.5.12 随时会冒出一个待更新,动手要趁早。

就地改一行,体外加一枚哨兵

Update URI 只认插件自己的文件头,这是唯一能让对面不再应答的位置,任何「另起一个插件」的方案都替代不了它。代价也很清楚:守卫和被守卫的东西写在同一个文件里。有人手动上传官方 zip 时(这一招 Update URI 挡不住),守卫会跟着改造一起消失。

所以最终是两处配合。插件体内改四行:加 Update URI: false、版本号抬到 1.5.12.1、补一段 FORK 注释指向改动台账。体外放一个约 30 行的 mu-plugin,每天比对三件事——Update URI 是否仍为 false、6 个关键文件的 md5、上游有没有发新版。官方包覆盖不会碰 mu-plugins/,它是唯一的外部锚点。

- * Version: 1.5.12
+ * Version: 1.5.12.1
  * Requires at least: 5.8
  * Requires PHP: 7.4
+ * Update URI: false
+ *
+ * XZ-FORK 1.5.12.1 — 本地改造版:已锁定 WordPress.org 更新
+ * 官方包直接覆盖会抹掉本行及全部本地改动,清单见 XZ-PATCHES.md

如果只想关闭 WordPress 插件自动更新、不打算做别的改动,上面那行 Update URI: false 就是全部所需——它同时也挡住了后台那个「更新」按钮的整包覆盖。

体外那枚哨兵不需要再写 auto_update_plugin 那一类过滤器。Update URI: false 生效之后,对面根本不返回条目,也就没有「自动更新对象」可供拦截,那层过滤是死代码。只留哨兵就够,它的价值不是拦截,而是在改造被覆盖时喊一声。

版本号后缀有个坑

我一开始想用 1.5.12-xz1 标记自己是改造版,实测不行。PHP 的 version_compare() 把连字符后面那段当预发布标签处理,1.5.12-xz1 对比官方的 1.5.12 返回 -1,也就是被判成更旧。1.5.12+xz1 同样返回 -1,只有 1.5.12.1 才返回 +1。

本地版本对比 1.5.12含义
1.5.12-xz1-1被判为更旧,危险
1.5.12+xz1-1同样被判为更旧
1.5.12.1+1正确的抬版方式

顺带说一个我放弃的方案:改目录名。它确实能挡住更新,但代价不小。WordPress 的 active_plugins 里记的是旧路径,改名之后插件会被静默停用;插件内部还有两处硬编码了 royal-mcp/royal-mcp.php 这个 basename,状态判断会跟着出错。一行文件头能解决的事,没必要去动目录。

删代码最怕的不是删错,是留下一堆空 if

集成适配器在 execute_tool() 里是三行一组的形态:判断工具名前缀、调用适配器、返回结果。用 sed 按别名批量删除,很容易只删掉 return 那一行,留下一个语法完全合法的空 if 块。php -l 查不出来,但该前缀的调用会静默穿透到后面的处理器。

所以这次一律整块删。每个适配器对应七行:use 语句、get_tools() 里的合并行、tool_requirements 映射、完整的 3 行 if、以及破坏性工具白名单里的一条。6 个自家插件适配器删完正好 42 行,对外工具数从 270 降到 240,tools/list 响应体从 264,192 字节降到 242,588 字节。

另一个数字更值得看:改造前 270 个工具里有 157 个指向本站根本没安装的插件,占 58%。原因是上游的 get_tools() 故意不按插件是否可用做短路,为的是避免工具在列表里忽隐忽现。设计上可以理解,但代价是每次 tools/list 都要全量传一遍用不上的定义。

汉化:语言包不能放插件目录

这个插件官方没有中文包,想给它做 WordPress 插件汉化只能自己动手。translate.wordpress.org 上这个项目完成度是 0%,语言包下载接口直接 404——顺手试了日文是 200,说明通道本身是通的,只是没人翻。好在上游的 i18n 做得规范,741 条唯一字符串都挂在同一个文本域下,make-pot 一次就能全抽出来。

关键发现是这个插件没有调用 load_plugin_textdomain()。这意味着它走的是 WordPress 的即时加载:WP_Textdomain_Registry 会主动扫描 wp-content/languages/plugins/ 找对应文件。所以语言包放在这里就生效,一行插件代码都不用改;但也必须放在这里,放进插件自己的 languages/ 目录会被官方包覆盖。

语言包位置会被官方包覆盖结论
plugins/royal-mcp/languages/会不要放
wp-content/languages/plugins/不会放这里

还有一条容易被忽略的连带后果:Update URI: false 之后,对面连这个插件的语言包更新也不再下发。实测 update_plugins 的 translations 列表里,本插件条目为 0。也就是说官方以后就算补了中文,也不会自动到站上,这套手动语言包是唯一通道,得自己维护。

翻译本身分三批推进,最终 682 / 741 条、92%。没译的 59 条是两类:54 条是 WP Abilities API 的能力分类 label 和 description,只给 AI 智能体读、不出现在任何后台界面,译成中文反而会改变智能体挑工具的依据;另外 5 条是插件头字段,WordPress 根本不会翻译它们,PO 里放了也没用。

一个翻不动的地方:写死在常量里的英文

全部翻完做复检,发现帮助页顶部 6 个标签还是英文。不是漏译,而是 Help_Page.php 第 62 行的 TABS 是一个 public const 数组,值全是纯英文字面量,压根不经过 __(),PO 拦不住。这类「翻译失效」和「漏译」在页面上长得一模一样,只能靠读源码区分。

PHP 的常量里不能放函数调用,所以只能改用法:在 render() 里对这份数组过一次翻译,再交给模板。只动模板拿到的那份副本,常量本身和键名都不变,tab 路由与 tab_url() 都不受影响。另外 Getting Started 这条 msgid 抽不出来(因为 msgid 是变量),手工补进了 PO。

$royal_mcp_help_tabs = array_map(
    static function ( $label ) {
        return __( $label, 'royal-mcp' );
    },
    self::TABS
);

这个例子值得记一下,因为它代表一类问题:只要字符串没有交给 __(),任何翻译工具都拿它没辙。反过来,凡是在界面上看到「其他都翻了、就这几处没翻」,先别怀疑语言包没生效,去源码里找它是怎么被输出的。

怎么确认「真的没有漏」

只跑 php -l 加肉眼看截图是不够的。语法通过只说明代码能跑,说明不了行为对;截图只能覆盖你打开的那一屏。这次分三层验证,每层解决一种盲区。

  • 真实入口抓 HTML:用 wp_generate_auth_cookie 生成短期登录 Cookie,配 COOKIEHASH,curl 加 --resolve 打回环地址,拿到后台的真实渲染结果。比在命令行里调过滤器可靠得多,后者还会因为主题的过滤器链直接致命错误。
  • 反向词表扫描:把 PO 里的英文原文当词表,在 strip_tags 之后的可见文本里逐个查找,命中就是残留。这样能覆盖到你没打开过的页面。
  • 只出现在特定状态的串单独测:后台通知、站点健康检测项、空表提示,这些在正常页面里根本不会渲染。必须用 wp eval-file 直接调 __()、或者把检测方法跑一遍,否则会误判成漏译。

最终结果:8 个后台页、6 个帮助标签页、4 项站点健康检测、20 条通知文案,英文残留 0。页面上剩下的英文只有三类——别的插件自己的通知、命令行片段、以及 Claude Desktop 这类产品名,都不该翻。

顺便交代一个自己犯的错。第二批翻译是按索引对齐的,中间漏了一条碎片 msgid(leave 被并进了上一句),整段往后错位一格。是逐条打印「原文 / 译文」对照才发现的——批量翻译一定要出对照表复核,不能只看条目数对得上就收工。

最终账目

指标改前改后
文件数 / 体积140 / 3,188 KB124 / 2,920 KB
Server.php 行数12,35712,316
对外工具数270240
tools/list 响应体264,192 B242,588 B
后台菜单项97
广告投放点90
中文覆盖0 / 741682 / 741(92%)
被官方覆盖的风险有已阻断

没做的那一档也交代一下。还剩 20 个第三方插件适配器(WooCommerce、Elementor、Yoast 那些),全删完工具能压到约 113 条、响应降到约 30 KB。但那样会改变对外的工具契约,已经连上的客户端要重新握手,收益是省流量、风险是打破现有调用。这笔账不划算,所以留着。

给同样想改插件的人三句

  • 先量清楚再动手。推广层往往比想象的更独立,而诊断逻辑常常比推广还大,删之前先分清哪部分是广告、哪部分是功能。
  • 想清楚守卫和被守卫要不要放同一个文件。Update URI 只能写在插件头,这一点没得选;但哨兵必须放在外面,否则出事时没人报信。
  • 别相信 php -l。它能告诉你语法对,不能告诉你行为对。空的 if 块、错位一格的翻译、只换 mo 不换 l10n.php,它全都放行。

改别人的代码这件事,我之前在主题上也干过:给 Sakurairo 的 H2 高亮条做减法,踩了三个 CSS 的坑,思路一样——先量清楚再动手。反过来的一篇是给 Sakurairo 写一个「延伸阅读」插件,自己写的东西就不存在被官方包覆盖这回事。

改完之后这个插件在后台已经完全看不出改造痕迹,除了设置页最下面多了一个「由 Royal MCP 提供」的中文页脚。它依然叫 Royal MCP、依然是 1.5.12.1,只是不会再自己变回去了。

分享内容:Royal MCP 插件汉化去广告:把 WordPress 插件改成自己的,官方更新再也覆盖不掉 分享链接:https://ug.link/xiaozou/filemgr/share-download/?id=08dec00e2dfa4a7795cccca15878e1b1 访问密码:k3be

如果你觉得这篇文章不错,有用的话可以点击下面的“赞赏”支持作者,你的支持是我更新的动力(饿饿吃饭)。

  • reward_image1
  • reward_image2
欢迎来到XiaoZou123,这里是一个电脑极客、数码爱好者网站。我平时喜欢关注数码新闻,研究计算机技术。如果你看我头像觉得我是二次元,那我其实还算不上!
最后更新于 2026-10-29