如何从零开发一款划词翻译的 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 自带的“侦探”。
想再升级?可以加个设置页面调整语言,或者弄个浮窗让翻译结果更好看。总之,动手试试,边做边学,插件开发其实挺有趣的!