资讯详情

资讯详情

零成本DIY:基于免费大模型API打造自己的浏览器翻译插件

先说说我为什么非要自己动手搭一个翻译插件。市面上的网页翻译工具不算少但真正能在“免费”和“好用”之间找到平衡的几乎没有。传统机翻插件翻出来的东西遇到“embedding”翻成“嵌入”问题不大遇到“fine-tune”翻成“微调”也能忍受最怕的是通篇生硬、术语前后不统一、长句子直接断成词对词的拼接。后来我开始用免费大模型 API 自己做一个网页翻译插件几个月用下来效果真的比我预想中稳定得多。这篇文章把整个搭建过程、代码细节、踩过的坑完整写出来适合想脱离付费插件、又不想被平台绑定的人参考零成本、可复用跑通之后你还能把同一个 API 用到句子润色、摘要生成、代码注释翻译等更多场景。1. 为什么自己搭翻译这件事真的被低估了1.1 传统机翻的真实痛点很多人觉得网页翻译就是把原文换一种语言打开插件、点一下按钮、完事。但实际操作过的人都知道机翻的体验远没有这么简单。英文网页里的长难句机翻经常把从句顺序硬掰读起来绕口专业术语在不同领域含义完全不同比如“stem”在植物学里是“茎”在语言学里是“词干”在机器学习里也可能是“主干网络”无脑直译基本都会翻车。更麻烦的是术语不一致的问题。同一篇技术文档里前文把“prompt”翻成“提示词”后文可能翻成“提示语”甚至“提醒”读者要自己猜测这两个词指的是同一个东西。传统统计翻译和早期的神经网络翻译对这个问题一直处理不好因为它们按句子独立翻译缺少全局视角。而大模型翻译天然具备上下文理解能力能根据整段、甚至整页的内容去判断某个词在当前场景下应该怎么译这是质的区别。1.2 大模型翻译到底强在哪我自己的体感是大模型翻译的强项在于“意译”而非“直译”。传统翻译系统更像是一本厚重的词典在做词序调整大模型则像一个有经验的译员它理解你想表达的意思再按照目标语言的表达习惯重新组织句子。举个例子我在技术文档里见过这样一句“The model card provides a standardized interface for reporting model performance across different benchmarks.”老式机翻会翻成“模型卡提供了一个标准化的接口用于报告模型性能跨不同基准。”意思对但中文读起来很别扭。大模型会翻成“模型卡定义了一套标准化的信息填写规范用来汇报模型在不同评测基准上的表现。”后者显然更像人写的。这种能力不是靠翻译规则堆出来的而是大模型在海量语料上训练出的语义理解能力。我们做的插件本质就是把这种能力接到浏览器里让用户在任何网页上划词、选句都能得到接近人工翻译质量的结果。而且因为是自己写的提示词、模型参数、翻译风格都可以随时改想让它翻得更正式还是更口语都是你说了算。1.3 零成本的底气免费大模型 API 额度解析说到“零成本”很多人第一反应是“免费的东西是不是不靠谱”。我一开始也有这个顾虑但实际算了一笔账之后就放心了。目前国内几家主流的国产大模型平台对新用户都提供相当可观的免费体验额度个人日常使用基本花不到钱。以我主力在用的 DeepSeek 开放平台为例新用户注册会赠送一定额度的 tokenAPI 按 token 计费价格本身就非常低。翻译一篇几千字的网页消耗的 token 可能才几万赠送额度足够测试和轻度使用很久。智谱的 GLM 系列开放平台也类似注册后有免费 token 用于调用月之暗面的 Kimi 开放平台同样有面向开发者的赠送额度。这里说的“零成本”指的是个人低频场景下用这些免费额度就能覆盖而不是平台在无限量白送。平台推荐模型免费额度情况适合场景DeepSeekdeepseek-chat注册赠送额度计费低通用翻译、长文本处理智谱glm-4-flash / glm-4-plus注册赠送 token中文优化、结构化输出Kimimoonshot-v1-8k新用户赠送额度长对话、上下文理解我建议第一版先用 DeepSeek因为它的 API 格式是 OpenAI 兼容的网上能参考的代码最多踩坑也容易找到答案。后面想换模型只需要改接口地址和模型名代码整体不用动。2. 技术方案选型三条路我为什么选浏览器扩展2.1 三条实现路径的对比目标是“网页翻译插件”但实现方式其实不止一种。我动手前先列了三个候选方案分别是油猴脚本、浏览器扩展、本地代理服务每个方案都实际试过一遍才确定了最终的路线。油猴脚本是最轻量的方案安装一个 Tampermonkey 插件然后往里面塞一段 JS 脚本就行不需要打包、不需要签名改代码也很快。缺点是权限和 UI 都很受限想让用户填 API Key、做设置面板、调样式都要靠 DOM 操作硬来脚本一多还容易跟页面脚本冲突。浏览器扩展是正统做法Chrome 和 Edge 都原生支持Manifest V3 提供了清晰的后台脚本、存储、弹窗能力用户安装体验好代码结构也干净。缺点是需要掌握扩展开发的几个核心概念上手成本比油猴脚本高一点点。本地代理服务最灵活你在本机跑一个 Python 或 Node 服务统一转发请求、缓存结果、做术语库管理扩展只负责 UI。但这对普通用户太不友好了——要先装环境、跑服务、开机自启任何一个环节出问题都很难排查。综合下来我选择了浏览器扩展。它介于两个极端之间代码复杂度可以接受用户使用体验最好而且在 MV3 架构下后台 Service Worker 可以用原生 fetch 去请求大模型 API配合扩展的跨域权限恰好绕开了网页环境里最麻烦的 CORS 限制。2.2 Manifest V3 带来的几个关键变化如果你之前查过浏览器扩展的旧教程大概率看到的是 Manifest V2 的写法后台页面是常驻的 background.html所有常驻脚本一直跑在内存里。Chrome 从 2023 年起逐步淘汰 MV2强制迁移到 Manifest V3所以现在写新插件必须用 V3 的方式。V3 最核心的变化有几点。第一后台脚本改成了 Service Worker不再是常驻页面脚本在空闲时会被浏览器回收事件触发时再唤醒所以你的后台代码必须是无状态的所有要保留的数据都得放到 storage 或 IndexedDB 里。第二权限模型更严格跨域请求需要在 host_permissions 里声明API Key 的存取也推荐用 chrome.storage 而不是 localStorage。第三不再支持远程托管代码所有 JS 都必须打进插件包里。这些变化刚开始会觉得麻烦但适应之后反而更安全。比如 Service Worker 的“睡眠-唤醒”机制本身就是一种资源保护我们翻译场景里每次请求是短连接很少碰到状态丢失的问题。只要记住一个原则状态全部持久化请求全部事件驱动MV3 开发就不会有大坑。2.3 整体架构与数据流整个插件的结构其实很简单由三个核心部分构成content script注入到网页里的 JS负责监听鼠标划词、展示翻译结果浮窗、以及向后台发送翻译请求。popup 弹窗用户点击工具栏图标后弹出的设置界面用来填 API Key、选目标语言、设置翻译风格。background Service Worker接收 content script 的消息统一调用大模型 API再把结果返回给页面。数据流是这样的用户在网页上划选一段文字 → content script 捕获选区 → 把文字和设置参数通过 chrome.runtime.sendMessage 发给后台 → Service Worker 收到消息读取存储里的 API Key发起 fetch 请求到大模型平台 → 平台返回翻译结果 → Service Worker 用 sendResponse 回传 → content script 在选区附近渲染一个浮窗展示译文。这里最容易被忽视的是“设置”的传递链路。弹窗里保存的 API Key 和目标语言存放在 chrome.storage.local 里content script 不能直接读 storage权限隔离所有请求都通过消息中转。我第一版想省事让 content script 直接 fetch结果被 CORS 拦得死死的后来才把请求全部改成后台转发。3. 实操从申请 API Key 到插件跑通3.1 第一步申请免费额度并拿到 API Key以 DeepSeek 为例整个过程五分钟左右。先去 DeepSeek 开放平台官网注册账号登录后在控制台左侧找到“API Keys”点创建新密钥复制保存好。这个 Key 就是你调用接口的凭证相当于银行卡密码一定不要泄露。关于免费额度的几个细节注册充值之前平台会赠送一定量的 token具体数量会随平台活动调整你以控制台显示为准。不建议一开始就充大额先用赠送额度跑通流程确认翻译质量满足需求后再小额充值充值几十块钱够用很久。还有一点DeepSeek 的 API 终端默认支持 OpenAI 格式所以你在网上看到的很多 OpenAI SDK 示例只要把 base_url 和 api_key 换掉就能直接用。其他平台的申请流程也大同小异智谱开放平台注册后创建 API Key模型推荐 glm-4-flash响应速度快价格也低Kimi 开放平台则要选好模型名和上下文长度。申请完把 Key 复制保存好下一步塞进插件设置里。3.2 第二步搭插件骨架文件一个最小可用的 MV3 浏览器扩展只需要三个文件manifest.json、background.js、content.js。我建了一个名为 ai-translator 的目录文件结构如下ai-translator/ ├── manifest.json ├── background.js ├── content.js ├── content.css ├── popup.html ├── popup.js └── icons/先写 manifest.json这是整个扩展的身份证{ manifest_version: 3, name: AI 网页翻译助手, version: 1.0.0, description: 基于免费大模型 API 的划词翻译与整页翻译插件, permissions: [storage, activeTab], host_permissions: [https://api.deepseek.com/*], background: { service_worker: background.js }, action: { default_popup: popup.html, default_icon: icons/icon16.png }, content_scripts: [ { matches: [all_urls], js: [content.js], css: [content.css] } ] }几个字段逐个说。permissions 里我只申请了 storage 和 activeTabstorage 用来存 API Key 和偏好设置activeTab 让插件在用户点击图标时才获得当前标签页的访问权限减少不必要的授权host_permissions 声明允许调用的域名这里写成 DeepSeek 的接口域名如果换平台记得改。content_scripts 里 matches 写成all_urls表示所有网页都注入脚本如果你的使用场景集中在某些网站收窄这个范围可以减少资源占用。3.3 第三步后台调用大模型 APIbackground.js 是整个插件的核心引擎。它要完成一件事接收 content script 传过来的翻译请求调用大模型接口把结果返回。代码大致如下chrome.runtime.onMessage.addListener((message, sender, sendResponse) { if (message.type translate) { handleTranslate(message.data) .then(sendResponse) .catch((error) sendResponse({ error: error.message })); return true; // 保持消息通道打开等待异步返回 } }); async function handleTranslate(data) { const { apiKey, model, text, targetLang, style } data; const response await fetch(https://api.deepseek.com/chat/completions, { method: POST, headers: { Content-Type: application/json, Authorization: Bearer ${apiKey} }, body: JSON.stringify({ model: model || deepseek-chat, temperature: 0.3, messages: [ { role: system, content: buildSystemPrompt(targetLang, style) }, { role: user, content: text } ] }) }); if (!response.ok) { const errData await response.json().catch(() ({})); throw new Error(errData.error?.message || HTTP ${response.status}); } const dataJson await response.json(); const translated dataJson.choices?.[0]?.message?.content?.trim(); if (!translated) { throw new Error(模型未返回有效的翻译结果); } return { translated }; }写后台脚本时最容易出错的是消息通道的关闭时机。Chrome 要求 if 分支里返回 true表示这个 sendResponse 是异步调用的等 handleTranslate 执行完再响应。如果你忘了 return true消息通道会提前关闭sendResponse 就石沉大海页面端永远等不到结果。这个错很隐蔽我第一次就栽在这里排查了大半天。3.4 第四步content script 实现划词翻译后台能把请求发出去还不够用户得有一个直观的交互方式。content script 负责监听鼠标划词动作在选区的右下方冒出一个“译”按钮点击后显示译文浮窗。let floatBtn null; document.addEventListener(mouseup, (event) { const selection window.getSelection(); const text selection?.toString().trim(); if (!text) { removeFloatBtn(); return; } const range selection.getRangeAt(0); const rect range.getBoundingClientRect(); showFloatBtn(rect.right, rect.bottom, text); }); function showFloatBtn(x, y, text) { removeFloatBtn(); floatBtn document.createElement(div); floatBtn.id ai-translator-float-btn; floatBtn.textContent 译; floatBtn.style.left ${x 8}px; floatBtn.style.top ${y 8}px; floatBtn.addEventListener(click, () { doTranslate(text); }); document.body.appendChild(floatBtn); } function removeFloatBtn() { if (floatBtn) { floatBtn.remove(); floatBtn null; } } async function doTranslate(text) { const { apiKey, targetLang, model, style } await chrome.storage.local.get([ apiKey, targetLang, model, style ]); if (!apiKey) { alert(请先在插件弹窗中配置 API Key); return; } const response await chrome.runtime.sendMessage({ type: translate, data: { apiKey, model, text, targetLang, style } }); if (response?.error) { alert(翻译失败${response.error}); return; } showTranslationResult(text, response.translated); }这里有一个需要注意的点content script 里的chrome.storage.local.get不能直接读到 popup 存储的数据吗其实是可以的content script 在 MV3 中能访问 storage API前提是 manifest 里声明了 storage 权限。我这个代码里读取设置是在 doTranslate 里做的权限没问题。浮窗样式在 content.css 里定义核心是 position: fixed、高 z-index、半透明背景避免被页面样式干扰。实际上线后你会发现部分网站自带 CSS 会覆盖插件浮窗样式所以建议在浮窗容器上使用高优先级选择器或者用 Shadow DOM 隔离。我第一版没用 Shadow DOM后来在几个样式复杂的网站上浮窗被压扁了逼着我重构了一次。3.5 第五步popup 设置面板与 API Key 保存popup.html 是用户看到的唯一界面我做得极简一个 API Key 输入框、一个目标语言下拉框、一个模型名输入框、一个翻译风格选择框、一个保存按钮。弹出面板时从 storage 里读取已有配置回填保存时写回 storage。!DOCTYPE html html head meta charsetUTF-8 / style body { width: 300px; padding: 12px; font-family: sans-serif; } label { display: block; margin-top: 10px; font-size: 13px; } input, select, button { width: 100%; padding: 6px; margin-top: 4px; } /style /head body labelAPI Key/label input typepassword idapiKey placeholdersk-... / label目标语言/label select idtargetLang option value中文中文/option option value英文英文/option option value日文日文/option /select label模型/label input typetext idmodel valuedeepseek-chat / label风格/label select idstyle option value通俗通俗/option option value专业专业/option option value学术学术/option /select button idsave保存/button script srcpopup.js/script /body /htmlpopup.js 负责两件事页面加载时回填配置点击保存时写入 storage。这个文件本身没什么难度但要注意一个交互细节popup 在用户点击页面其他位置时会自动关闭所以配置填写过程中如果切换了标签页未保存的内容就丢了。我给输入框加了 input 事件每次输入都自动保存用户不需要手动点保存按钮更加省心。还有个安全细节API Key 输入框用 typepassword显示为密文避免旁人窥屏。但真正防泄露的关键是不要把 Key 硬编码在代码里而是存在 storage 中并且不主动打印到控制台。3.6 安装测试加载已解压的扩展开发完成后的安装测试很简单。打开 Chrome 或 Edge地址栏输入chrome://extensionsEdge 是edge://extensions打开右上角的“开发者模式”点击“加载已解压的扩展程序”选择 ai-translator 目录。此时浏览器工具栏会出现插件图标点击图标填写 API Key 和语言设置然后到任意英文网页划词试试。第一次测试我建议用一段包含专业术语和长从句的英文段落。如果浮窗能弹出、结果能在两秒内返回、术语翻得准确那整个链路就算通了。如果请求失败多半是 API Key 填错、域名没配权限、或者消息通道关闭时机不对这几个问题下一节专门讲。4. 常见问题与排查技巧实录4.1 硬坑CORS、消息通道和模型名导致的请求失败很多卡在第一步的人问题都出在“请求发出去了但没收到结果”。我把常见现象和原因列成了一张速查表方便你对照排查现象大概率原因解决办法控制台报 CORS errorcontent script 直接发起了 fetch把请求全部放到 background.js用消息转发弹窗里提示“message channel closed”onMessage 回调里没有 return true异步回调前显式 return true报 404 或 invalid model模型名写错或平台不支持该模型到平台控制台核对可用模型名401 UnauthorizedAPI Key 错误或已过期重新生成 Key 并在插件里更新429 Too Many Requests触发了平台的限流策略降低请求频率或换用低并发模型关于 CORS 多说一句。浏览器扩展里content script 由于运行在网页上下文受到网页同源策略约束直接 fetch 大模型接口大概率被拦。解决办法就是让后台 Service Worker 代为请求因为扩展后台有 host_permissions 里的跨域授权不受网页 CORS 限制。这个设计不是绕过的漏洞而是浏览器官方给扩展开发者的合法能力。4.2 API Key 泄露风险与存储策略很多人觉得 API Key 存在本地插件里很安全其实是侥幸心理。浏览器扩展的 storage 虽然是浏览器沙箱但如果你把插件打包分发给别人用户打开开发者工具就能看到明文 Key。更危险的是如果你把 Key 写死在代码里再发布到应用商店别人下载后可以直接提取你的 Key 拿去盗刷。我的做法是本机自用插件Key 存 chrome.storage.local不参与任何分享如果要分享给他人使用方案改成让用户在弹窗里填自己的 Key每个人都用自己的额度。Key 输入框用 password 类型storage 层不主动打印日志调试时也不把完整 Key 输出到控制台。另外如果发现 Key 泄露立即到平台控制台把它删掉重新生成一个新的旧 Key 就失效了。4.3 上下文长度超限与限流处理翻译长网页时常常会遇到“maximum context length exceeded”之类的报错本质是把整页文本一次性塞进请求超出了模型的最大 token 限制。解决思路是分块按段落或按句子拆开每块独立翻译再拼接结果。分块还有一个好处每块更小模型对上下文的注意力更集中翻译质量反而更好。分块逻辑我建议按“句子边界”拆分。中文按句号、问号、感叹号切分英文按英文句点切分注意避免把 URL、小数点和缩写断开。拆完之后可以用 Promise.all 并发发送多个请求但要注意平台的 QPS 限制并发太猛容易被限流。我实际测试下来10 个小块并发基本稳定再大就要排队处理了。另一个相关的坑是“限流”和“额度耗尽”的区别。429 是因为单位时间内请求太多被限流而“insufficient balance”或“quota exceeded”说明免费额度用完了。前者的解决办法是减缓请求频率、加退避重试后者只能去平台充一点钱或等额度刷新个人使用的话充十块钱能用很久。4.4 翻译质量调优提示词与术语表的工程化很多人用大模型 API 只做简单地把 text 塞进 user 消息其实翻译质量的高低一半取决于模型另一半取决于提示词。我现在的系统提示词是这样的你是一名专业的技术文档翻译专家。你的任务是把用户提供的内容翻译成目标语言。 要求 1. 保持原文的含义、语气和格式 2. 专业术语必须统一前后文一致 3. 代码块、网址、变量名不要翻译 4. 中文表达要自然流畅避免直译和生硬的语序 5. 如果是表格或列表保持原有结构。这段提示词的要点是明确定义角色、列出硬性规则、给出反例般的细节约束。模型对“保持格式”“术语统一”这样的具体要求执行得比泛泛的“请翻译一下”好很多。更进一步如果你翻译的领域非常固定比如只翻译专利文献或者医学论文可以把领域术语表写进 system 提示词例如“当出现 claim 时译为权利要求出现 embodiment 时译为实施例”。模型的执行能力很强术语表给得越明确输出越稳定。temperature 参数我也建议固定在 0.3 左右。temperature 太高会让翻译变得天马行空太低又会过于机械。0.3 是我反复对比后的平衡点生成速度和质量都理想。最后多说几句实践经验整套流程走下来我最大的感受是用免费大模型 API 做翻译插件不只是省了订阅费更重要的是把“翻译”这件事的控制权拿回到了自己手里。翻译粒度、风格、术语、甚至 UI 呈现方式全部可以按自己的使用习惯调整。我现在这个插件已经扩展出了整页翻译和双语对照模式看英文技术文档时先划词确认个别术语再用整页翻译扫一遍上下文体验比很多商业插件舒服得多。最后分享一个提升幸福感的小技巧可以在后台把每次翻译结果和原文一起写入浏览器的 IndexedDB积累到一定量后你会拥有一份专属于自己的双语语料库。拿它来做词汇复习、做术语表的自动提取甚至训练一个小型术语记忆模型都是很好的后续扩展方向。翻译工具只是起点数据积累之后的玩法才是这个项目最值钱的部分。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →