ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

OpenClaw界面汉化:Tampermonkey脚本精准中文化实战

OpenClaw界面汉化:Tampermonkey脚本精准中文化实战 前面有人问过我部署好OpenClaw之后第一件让人头疼的事是什么我的答案不是模型配额不是skill配置而是那个全英文的管理界面。我自己的OpenClaw实例跑起来之后每次调整代理参数、查看任务状态都要在英文界面里翻半天。网页翻译插件试过一键翻译确实快但把代码里的变量、按钮里的英文全给翻了点了还容易出问题。后来我花了两个晚上用Tampermonkey脚本给OpenClaw界面做了精准中文化只翻译界面文案不动代码逻辑效果比我预想中好得多。这篇就把我的完整思路、踩过的坑和可直接用的脚本框架写出来给有同样需求的人一个参考。1. 为什么最终选了Tampermonkey脚本而不是改源码或浏览器翻译1.1 先说说我的三个方案对比OpenClaw界面中文化的路线其实不止一条。我最早的想法是直接改OpenClaw的前端源码把语言包里的英文文案全部替换成中文。这个方案听起来最干净但实际操作会碰到几个绕不开的问题OpenClaw升级之后源码会被覆盖每次升级都要重新改一遍本地改动需要重新构建前端资源如果你部署的是Docker版本还得把构建产物重新打镜像更麻烦的是OpenClaw的界面文案并不全在一个语言包里不少是后端传回来的动态字符串光改前端根本覆盖不全。第二个方案是浏览器自带的翻译功能。Edge、Chrome的网页翻译对OpenClaw这种单页应用不算友好页面加载后翻译一次你切到某个Tab或者打开弹窗新渲染出来的内容还是英文得手动再翻一次。而且翻译引擎对DeployRunStatus这类短词的翻译经常不太对劲甚至会把API路径里的单词也翻掉造成界面信息失真。最后我选择了Tampermonkey脚本。它不需要碰OpenClaw任何源码升级不受影响脚本在浏览器端运行对每一帧界面都有控制权替换规则完全由我定义真正做到只翻译该翻译的。从长期维护角度看Tampermonkey脚本是最划算的路线。1.2 Tampermonkey脚本在OpenClaw汉化里的工作方式Tampermonkey脚本本质上就是一个浏览器端JavaScript注入器。它通过脚本头部的match规则声明自己只跑在OpenClaw管理界面的域名下通过run-at设置注入时机在页面文档解析完成后执行。对于OpenClaw这种大量使用异步渲染的单页应用脚本还需要搭配MutationObserver去监听DOM变化这样才能把动态加载出来的英文文案也一并处理掉。这里有个关键认知我做的不是翻译而是界面文案映射。我的脚本维护一张中英文对照表遍历OpenClaw界面里可见的文本节点命中英文词条就替换成对应的中文。这样做的好处是精确——不经过翻译引擎不存在上下文语义问题也不会误杀代码内容。代价是词条需要手工维护OpenClaw界面更新新增的文案需要你自己补充。2. 梳理OpenClaw界面文案定位导航、按钮、状态提示三类核心区域2.1 先决定中文化的覆盖范围在写任何代码之前我做的第一件事是把OpenClaw管理界面的文案分布摸清楚。我用的部署版本界面结构大致分这几块顶部导航栏、左侧功能菜单、中间的工作区、底部的状态信息栏以及各种弹出的配置对话框。不同区域的文案性质不一样优先级也不一样。我给自己定的覆盖顺序是高频操作区优先。左侧菜单里的代理管理、任务列表、技能配置这些入口是每天都要点的地方文案不理解会直接影响操作效率其次是配置弹窗里的表单标签和按钮比如SaveCancelAPI Key这类文案填错代价高最后才是状态提示区像RunningCompletedFailed这类状态词配合颜色图标其实猜也能猜个大概但翻成中文会更直观。2.2 用开发者工具快速提取文案清单这部分是纯体力活但对后续写替换规则至关重要。我打开Chrome DevTools用Elements面板逐个区域点开查看把界面里出现的英文文案和DOM结构对应起来记录下来。我的记录方式是建一个表格每一行是一个词条英文原文、中文译法、出现位置、元素特征。英文原文中文译法出现位置元素特征Dashboard仪表盘左侧导航.nav-itemAgents代理列表左侧导航.nav-itemDeploy部署工作区按钮button.btn-primaryRunning运行中任务状态标签.status-badgeConfiguration配置顶部Tab.tab-item除了肉眼看到的静态文案我还用Network面板观察了页面加载和交互时的XHR请求。OpenClaw界面不少文案是后端接口返回后在浏览器端渲染出来的这些动态文案在初始DOM里不存在只有在触发某些操作后才出现。比如任务详情弹窗里的一段英文描述就是点开弹窗时通过接口拉取的。这类文案必须靠MutationObserver监听才能覆盖到我会在后面的实现部分详细讲。3. 中文化脚本的核心实现字典表、文本节点替换和动态内容监听3.1 脚本骨架与关键配置我直接给出脚本的基础骨架这份代码框架我在OpenClaw当前版本上验证过可以直接用。// UserScript // name OpenClaw 界面中文化 // namespace https://your-namespace.example/ // version 0.1.0 // description OpenClaw管理界面精准中文化脚本 // author your-name // match http://localhost:3000/* // match https://openclaw.example.com/* // run-at document-idle // grant GM_addStyle // /UserScriptmatch字段要替换成你自己的OpenClaw管理界面地址。如果你用的是本地默认端口http://localhost:3000/*一般够用如果是远程部署把域名写进另一行match规则里就行。这里有一个容易被忽略的细节run-at document-idle表示DOM解析完成后执行但OpenClaw是单页应用首次加载后还会继续渲染所以脚本不能只跑一次后面需要用MutationObserver持续监听。grant GM_addStyle是用来注入自定义样式的我在处理中文字体回退时会用到它。3.2 字典表与文本节点替换函数字典表我建议用一个Map结构维护比普通对象更适合做大量词条匹配。替换逻辑的核心是遍历文本节点命中词典后替换textContent。const dict new Map([ [Dashboard, 仪表盘], [Agents, 代理列表], [Deploy, 部署], [Running, 运行中], [Configuration, 配置], [Save, 保存], [Cancel, 取消], ]); function translateNode(node) { if (node.nodeType Node.TEXT_NODE) { const text node.nodeValue; if (text text.trim()) { for (const [en, zh] of dict.entries()) { if (text.includes(en)) { node.nodeValue text.split(en).join(zh); } } } } else if ( node.nodeType Node.ELEMENT_NODE ![SCRIPT, STYLE, CODE, PRE].includes(node.tagName) ) { node.childNodes.forEach(translateNode); } }这个函数有几个设计决策值得说明。第一判断节点类型时我只处理TEXT_NODE不处理属性节点避免把href、class这些属性里的英文单词也翻译掉。第二SCRIPT、STYLE、CODE、PRE这些标签里的内容天然需要跳过否则脚本代码块里的英文会被误伤。第三我用split加join的方式做整词替换而不是正则主要是性能考虑——在遍历DOM节点时字符串拆分方式比编译正则更快也更安全。3.3 用MutationObserver解决异步渲染漏译问题OpenClaw界面大量使用前端框架渲染第一次脚本执行完后用户点击按钮、切换Tab都会触发新DOM节点插入。要覆盖这些动态内容MutationObserver是标配方案。const observer new MutationObserver((mutations) { let shouldTranslate false; for (const mutation of mutations) { if (mutation.type childList mutation.addedNodes.length 0) { shouldTranslate true; break; } } if (shouldTranslate) { translateNode(document.body); } }); observer.observe(document.body, { childList: true, subtree: true, });这里有个性能细节MutationObserver回调在每次DOM变化时都会触发如果每次触发都做全量文本遍历OpenClaw这种高频刷新界面会有明显卡顿。我的做法是先判断addedNodes是否有新增节点有才执行翻译而且翻译函数本身有节点类型过滤能省掉大量无意义遍历。如果你发现界面交互时脚本还有性能问题可以在回调里加一个setTimeout做节流把多次DOM变化合并成一次翻译操作。4. 实测中的意外选择器失效、重复替换、字体渲染与性能问题4.1 重复替换问题同一段英文被翻译两次我的脚本第一版跑起来后很快发现一个诡异的问题部分中文文案会叠加。比如仪表盘变成了仪表盘仪表盘。排查后发现原因很简单——MutationObserver触发了两次翻译第一次把Dashboard替换成仪表盘第二次遍历时字典里没有仪表盘但文本节点里已经没有英文词条了理论上不该有问题。问题出在OpenClaw某些组件会重新渲染整个DOM子树这个过程中旧节点被移除、新节点被插入如果新节点里的文案已经是中文我的脚本就不该再处理可实测中还是会重复处理。解决方案是给文本节点加一个自定义标记function translateNode(node) { if (node.nodeType Node.TEXT_NODE) { if (node.parentElement node.parentElement.dataset.translated true) { return; } const text node.nodeValue; if (text text.trim()) { let changed false; for (const [en, zh] of dict.entries()) { if (text.includes(en)) { node.nodeValue text.split(en).join(zh); changed true; } } if (changed node.parentElement) { node.parentElement.dataset.translated true; } } } }这个标记的含义是这个节点已经被脚本处理过不要重复翻译。它的原理是利用DOM元素上的>GM_addStyle( .btn { min-width: 88px; padding-left: 16px; padding-right: 16px; } .status-badge { white-space: nowrap; padding: 2px 10px; } );这套样式只针对我确认过会溢出的几个组件类名做了微调没有全局改变布局。这里要注意的是选择器一定要和OpenClaw实际使用的类名一致不同版本的类名可能不同建议用DevTools确认后再写进样式里。如果不想维护这么多规则也可以用font-family全局指定中文字体优先让中文渲染更紧凑这个方案更省事但也更依赖系统字体环境。4.4 频繁DOM操作导致的界面卡顿脚本进入正常使用阶段后我注意到OpenClaw在展示任务日志的页面有明显卡顿滚动时帧率下降。用Performance面板分析后发现高频日志输出会不断触发MutationObserver每次触发都执行全量翻译导致主线程被大量的DOM操作占用。最终我采用了两管齐下的优化方案。箭头方向有两个优化在MutationObserver回调中加节流控制多个DOM变化合并成一次翻译操作避免重复遍历。缩小遍历范围。把翻译的目标从document.body收缩到mutation.target所在的局部区域新增节点通常就在这个区域内不需要全页扫描。let translateTimer null; const observer new MutationObserver((mutations) { if (translateTimer) return; translateTimer setTimeout(() { for (const mutation of mutations) { mutation.addedNodes.forEach((node) { if (node.nodeType Node.ELEMENT_NODE) { translateNode(node); } }); } translateTimer null; }, 200); });这个改法让脚本在日志高频刷新时基本不再影响页面流畅度。核心思路是批量处理局部扫描这在处理任何动态渲染密集的界面时都适用。5. 把这个脚本变成可维护的小工具版本管理与规则升级思路5.1 字典外置让词条维护不再痛苦脚本使用一段时间后你会面临一个现实问题OpenClaw升级了界面多了几个新的英文词条怎么办如果字典表直接写在脚本主体里每次都改脚本代码违背了脚本管理的初衷。我采用的方式是把字典表独立成一份配置在脚本运行时动态合并。const DICT_URL https://your-host.com/openclaw-zh-dict.json; async function loadDict() { const resp await fetch(DICT_URL); const remoteDict await resp.json(); Object.entries(remoteDict).forEach(([en, zh]) { dict.set(en, zh); }); }远程字典的好处是词条更新不需要用户重新安装或刷新脚本脚本启动时会自动拉取最新词条。我实际用下来这个方案最适合服务端集中维护有人发现漏翻或者错翻直接改JSON文件所有用户次日启动脚本就自动同步。需要注意一点如果OpenClaw管理界面所在的域名和字典所在的域名不同脚本会跨域请求需要在Tampermonkey的权限设置里给脚本开通对应的跨域访问权限。5.2 调试技巧用日志模式快速定位问题调试中文化脚本有一个很实用的技巧在脚本里增加一个调试开关通过日志查看哪些节点被翻译了、哪些被跳过了。我在浏览器console里用console.debug输出被替换的词条和对应DOM路径配合Tampermonkey的脚本控制台功能能快速定位漏译和误翻的位置。function logTranslate(en, zh, el) { if (debugMode) { console.debug([OpenClaw-zh] ${en} - ${zh}, el); } }调试模式建议只在开发和排障时开启正式使用时关掉否则高频率的日志输出也会造成可感知的性能消耗。5.3 分享与版本发布的经验用着顺手之后我把它整理发布到了脚本分享平台。如果你是第一次发布有几个细节值得注意脚本头部version字段务必遵循语义化版本规则主版本号.次版本号.修订号Tampermonkey会依据这个值判断是否有更新updateURL要指向脚本文件的最新地址这样用户安装后能自动收到更新提示描述字段里建议写清楚脚本适用的OpenClaw版本避免新版本界面不兼容时误装。清理不必要的match规则同样重要。发布版本里我只保留了一行通用域名规则把调试期的localhost和临时IP都删掉了。这既是安全考虑也是为了让脚本更聚焦——它只运行在OpenClaw管理界面不会干扰其他站点。我个人的体会是界面中文化这件事看起来小但牵扯的问题其实不少。你要同时处理DOM遍历、动态渲染、性能优化、样式兼容还得预留后续维护的通道。从一个简单的翻译按钮到一套可长期使用的汉化工具中间这些细节就是差距所在。如果你也在给OpenClaw或者其他英文界面做汉化不妨从上面这套框架起步踩过的坑我都写在前面了能帮你少走不少弯路。
返回列表