如何从零开发一款划词翻译的 Chrome 插件
Chrome 浏览器凭借其强大的扩展生态,让我们可以轻松定制功能来提升使用体验。划词翻译插件就是其中一个实用的小工具:选中网页上的文字,就能立刻看到翻译结果,特别适合阅读外文网页时快速理解内容。本文将带你从零开始,打造一款划词翻译插件,包含基础的翻译功能,甚至还能加上生词本和高亮生词的小惊喜。别担心,我会尽量把每一步讲得清楚明白,像朋友聊天一样带你入门。
为什么要做这个插件?
想象一下,你在看一篇英文文章,碰到不认识的单词,不用打开新标签页查字典,直接鼠标一划,翻译就跳出来,多方便!Chrome 扩展的开发其实没那么神秘,只要掌握几个核心概念,就能做出实用的工具。咱们的目标是:选中文字后翻译,翻译结果显示在页面上,还能把生词存下来,下次看到时高亮提醒。
准备工作:工具和环境
动手前,先准备好这些家伙:
- Chrome 浏览器:开发和测试的主场,必须得有。
- 文本编辑器:推荐 Visual Studio Code,轻便好用,写代码调试都顺手。
- 基础知识:懂点 JavaScript、HTML 和 CSS 就够了,不需要高深技术。
装好这些,找个安静的角落,咱们开始干活!
第一步:搭建项目框架
创建项目文件夹
在电脑上建个文件夹,比如叫 chrome-translate-extension,用来放所有文件。简单点,里面会塞几个关键文件:配置文件、脚本啥的。
配置 manifest.json
每个 Chrome 扩展都得有个“身份证”——manifest.json。新建这个文件,写上以下内容:
{
"manifest_version": 3,
"name": "划词翻译",
"version": "1.0",
"description": "选中网页文本并翻译",
"permissions": ["activeTab", "https://translation.googleapis.com/*"],
"background": {
"service_worker": "background.js"
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"]
}
]
}这文件啥意思呢?
manifest_version: 3:Chrome 现在推荐用版本 3,规则更现代。name和description:插件的名字和简介,随手取个明白的。permissions:告诉 Chrome 我们需要啥权限,比如访问当前标签页和调用翻译 API 的网络权限。background:指定背景脚本background.js,它像个后台管家,处理翻译请求。content_scripts:定义内容脚本content.js,它会在网页上跑,负责抓取你选中的文字。
第二步:实现核心功能
插件的灵魂是“选中文字→翻译→显示结果”,咱们拆成三部分搞定。
内容脚本:抓取选中文本
新建 content.js,写上这些代码:
document.addEventListener("mouseup", () => {
const selectedText = window.getSelection().toString().trim();
if (selectedText) {
chrome.runtime.sendMessage({ action: "translate", text: selectedText });
}
});
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.action === "showTranslation") {
const div = document.createElement("div");
div.style.position = "fixed";
div.style.top = "10px";
div.style.right = "10px";
div.style.backgroundColor = "white";
div.style.padding = "10px";
div.style.border = "1px solid black";
div.style.zIndex = "9999";
div.textContent = request.translation;
document.body.appendChild(div);
}
});- 监听鼠标松开:用
mouseup事件,鼠标一松就看看你选了啥文字。 - 发送消息:如果真选了文字,就通过
chrome.runtime.sendMessage发给背景脚本,带着“translate”标签和选中的内容。 - 接收翻译:背景脚本翻译完会发回结果,咱们监听消息,收到后弄个小方框显示在页面右上角。
背景脚本:调用翻译 API
接着建 background.js,负责翻译的“大脑”:
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.action === "translate") {
const apiKey = "YOUR_API_KEY"; // 替换成你自己的密钥
const url = `https://translation.googleapis.com/language/translate/v2?key=${apiKey}`;
const data = {
q: request.text,
target: "en", // 翻译成英文
};
fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data),
})
.then((response) => response.json())
.then((result) => {
const translatedText = result.data.translations[0].translatedText;
chrome.tabs.sendMessage(sender.tab.id, {
action: "showTranslation",
translation: translatedText,
});
})
.catch((error) => console.error("翻译失败:", error));
}
});- 监听消息:收到内容脚本的“translate”请求后,干活。
- 调用 API:用 Google Translate API,发送选中的文字,目标语言设为英文(可以改成别的)。
- 返回结果:翻译完后通过
chrome.tabs.sendMessage发回内容脚本。
API 密钥咋弄? 去 Google Cloud Console,建个项目,启用 Cloud Translation API,生成密钥,填到 YOUR_API_KEY 里。别忘了开通计费,不然用不了。
第三步:加点高级功能
基础功能有了,咱们再锦上添花。
生词本:存下不认识的词
用 IndexedDB 做个小数据库,存生词。改 background.js,加这些代码:
const DB_NAME = "VocabularyDB";
const STORE_NAME = "words";
function openDB() {
return new Promise((resolve, reject) => {
const request = indexedDB.open(DB_NAME, 1);
request.onupgradeneeded = (event) => {
const db = event.target.result;
if (!db.objectStoreNames.contains(STORE_NAME)) {
db.createObjectStore(STORE_NAME, { keyPath: "original" });
}
};
request.onsuccess = (event) => resolve(event.target.result);
request.onerror = (event) => reject(event.target.error);
});
}
function addWord(original, translation) {
openDB()
.then((db) => {
const transaction = db.transaction([STORE_NAME], "readwrite");
const store = transaction.objectStore(STORE_NAME);
store.put({ original, translation });
})
.catch((error) => console.error("存储失败:", error));
}
// 加到消息监听里
if (request.action === "addToVocabulary") {
addWord(request.original, request.translation);
}- IndexedDB:浏览器自带的小型数据库,比 localStorage 更强大,适合存结构化数据。
- 存储逻辑:收到“addToVocabulary”消息,把原文和翻译存起来。
高亮生词:在页面标记
改 content.js,让页面加载时高亮生词:
function highlightWords(vocabulary) {
if (isHighlighting) return;
// 提取生词的原始文本
const words = vocabulary.map((item) => item.original).filter(Boolean);
if (!words.length) return;
isHighlighting = true;
try {
// 创建一个正则表达式匹配所有单词
const escapedWords = words.map((word) => word.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
const regex = new RegExp(`\\b(${escapedWords.join("|")})\\b`, "gi");
console.log("regex: ", regex);
// 使用TreeWalker遍历文本节点
const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, {
acceptNode(node) {
const parent = node.parentNode;
// 跳过已处理的节点和特殊标签
if (
!parent ||
parent.nodeName === "SCRIPT" ||
parent.nodeName === "STYLE" ||
parent.nodeName === "MARK" ||
parent.id === "translation-popup" ||
highlightedNodes.has(parent)
) {
return NodeFilter.FILTER_REJECT;
}
return NodeFilter.FILTER_ACCEPT;
},
});
let textNode;
const nodesToProcess = [];
// 收集需要处理的节点
while ((textNode = walker.nextNode())) {
if (regex.test(textNode.textContent)) {
nodesToProcess.push(textNode);
}
}
// 处理收集到的节点
nodesToProcess.forEach((node) => {
const text = node.textContent;
const container = document.createElement("span");
let lastIndex = 0;
regex.lastIndex = 0;
let result;
let processedText = "";
while ((result = regex.exec(text)) !== null) {
// 添加匹配前的文本
processedText += text.slice(lastIndex, result.index);
// 添加带高亮的匹配文本
const title = vocabulary.find((item) => item.original === result[0]).translation;
processedText += `<mark title="${title}" class="abei-highlighted-word">${result[0]}</mark>`;
lastIndex = regex.lastIndex;
}
// 添加剩余文本
processedText += text.slice(lastIndex);
container.innerHTML = processedText;
if (node.parentNode) {
node.parentNode.replaceChild(container, node);
highlightedNodes.add(container);
}
});
} catch (error) {
console.error("高亮处理出错:", error);
} finally {
isHighlighting = false;
}
}- 遍历文本:用
TreeWalker扫描页面所有文字节点。 - 正则匹配:找到生词后,用
<mark>标签高亮。
第四步:调试与测试
代码写好了,得试试管不管用。
加载扩展
- 打开 Chrome,输入
chrome://extensions/。 - 点右上角“开发者模式”开关。
- 点击“加载已解压的扩展程序”,选你的项目文件夹。
调试技巧
- 内容脚本:在网页上右键“检查”,打开控制台,看看
console.log输出啥。 - 背景脚本:在扩展页面点“背景页”,打开它的开发者工具。
- 加日志:代码里多写点
console.log,追踪哪里出了问题。
试着选几段文字,看翻译跳不跳出来;存几个生词,刷新页面看高不高亮。出错了别慌,多打印日志慢慢查。
总结:收获与下一步
到这步,你已经做出了一个挺像样的划词翻译插件!它能翻译文字、存生词、还会在页面上高亮提醒。过程中你接触了:
- manifest.json:扩展的“说明书”。
- 内容脚本:跟网页互动的“前台”。
- 背景脚本:处理逻辑的“后台”。
- IndexedDB:存数据的“小仓库”。
- 调试工具:Chrome 自带的“侦探”。
想再升级?可以加个设置页面调整语言,或者弄个浮窗让翻译结果更好看。总之,动手试试,边做边学,插件开发其实挺有趣的!