
探索 Chrome 最新的 Translator API ,实现离线、高效、隐私友好的本地翻译功能
本文基于 Chrome 131 Canary 版本编写,API 可能会随版本更新而变化。
第一次使用可能需要下载模型 点击查看在线演示 →

在 Web 开发中,我们通常使用以下翻译方案:
云端翻译服务( Google Translate API 、百度翻译等)
静态国际化文件( i18n )
Chrome 推出的 Translator API 是浏览器内置的本地翻译解决方案,带来了革命性的改变:
| 特性 | Translator API | 云端翻译 | 静态 i18n |
|---|---|---|---|
| 离线支持 | 完全离线 | 需要网络 | 离线可用 |
| 隐私保护 | 数据不出浏览器 | 数据传输到服务器 | 无数据传输 |
| 动态翻译 | 实时翻译任意文本 | 实时翻译 | 仅预定义文本 |
| 响应速度 | 极快(本地计算) | 受网络影响 | 极快 |
| 成本 | 免费 | API 调用收费 | 免费 |
| 维护成本 | 低 | 低 | 高(多语言文件) |
目前 Translator API 正处于实验性阶段,支持情况如下:
提示:虽然目前是实验性功能,但 Chrome 团队正在积极推进标准化,预计未来将成为 Web 标准的一部分。
Translator API 是基于浏览器内置的神经网络翻译模型,能够在本地完成高质量的文本翻译。它是 Chrome AI 计划的一部分,与 Prompt API 、Summarizer API 等共同构成浏览器端 AI 能力。
// 检查浏览器是否支持 Translator API if (!('Translator' in self)) { console.error('当前浏览器不支持 Translator API'); } // 检查特定语言对的可用性 const availability = await self.Translator.availability({ sourceLanguage: 'en', targetLanguage: 'zh' }); console.log(availability); // 可能的返回值: // - "unavailable" : 用户的设备或所请求的会话选项不受支持。设备可能电量不足或磁盘空间不足 // - "downloadable" : 需要进行额外的下载才能创建会话。可能需要用户激活才能调用 create() // - "downloading" : 下载正在进行中,必须先完成下载,然后才能使用会话 // - "available" : 您可以立即创建会话 返回值说明:
"unavailable" - 用户的设备或所请求的会话选项不受支持。设备可能电量不足或磁盘空间不足"downloadable" - 需要进行额外的下载才能创建会话,这可能包括专家模型、语言模型或微调。可能需要用户激活才能调用 create()"downloading" - 下载正在进行中,必须先完成下载,然后才能使用会话"available" - 您可以立即创建会话const translator = await self.Translator.create({ sourceLanguage: 'en', // 源语言( ISO 639-1 代码) targetLanguage: 'zh' // 目标语言( ISO 639-1 代码) }); 参数说明:
sourceLanguage: 源语言代码(如 'en', 'zh', 'ja')targetLanguage: 目标语言代码const result = await translator.translate('Hello, world!'); console.log(result); // "你好,世界!" 使用 BCP 47 语言短代码作为字符串。例如,'es' 表示西班牙语,'fr' 表示法语。
目前支持的主流语言(不完全列表):
| 语言 | ISO 代码 | 语言 | ISO 代码 |
|---|---|---|---|
| 中文(简体) | zh 或 zh-CN | 英语 | en |
| 日语 | ja | 韩语 | ko |
| 法语 | fr | 德语 | de |
| 西班牙语 | es | 俄语 | ru |
| 意大利语 | it | 葡萄牙语 | pt |
| 阿拉伯语 | ar | 印地语 | hi |
注意:具体支持的语言对可能因 Chrome 版本而异,建议使用前通过
availability()检测。
传统方案(云端 API ):
// 需要调用外部 API const respOnse= await fetch('https://api.translate.com/v1/translate', { method: 'POST', body: JSON.stringify({ text, from: 'en', to: 'zh' }) }); const result = await response.json(); 问题:
本地 AI 方案:
const translator = await self.Translator.create({ sourceLanguage: 'en', targetLanguage: 'zh' }); const result = await translator.translate(text); 优势:
传统方案( i18n 文件):
// en.json { "welcome": "Welcome", "description": "A translation demo" } // zh.json { "welcome": "欢迎", "description": "翻译演示" } // 使用 document.getElementById('title').textCOntent= i18n.t('welcome'); 问题:
本地 AI 方案(动态翻译):
// 直接翻译页面上的所有文本 async function translatePage(targetLang) { const translator = await self.Translator.create({ sourceLanguage: 'zh', targetLanguage: targetLang }); const elements = document.querySelectorAll('[data-i18n]'); for (const el of elements) { el.textCOntent= await translator.translate(el.textContent); } } 优势:
基于我开发的 Demo (translator-demo.html),我将详细介绍如何从零构建一个完整的翻译应用。
下载 Chrome Canary 或 Dev 版本
启用实验性功能
打开以下两个 Chrome flags:
chrome://flags/#translation-api chrome://flags/#optimization-guide-on-device-model 设置为 Enabled,然后重启浏览器。
在应用启动时,首先检测 API 是否可用:
async function checkAPIAvailability() { try { // 1. 检查浏览器是否支持 Translator API if (!('Translator' in self)) { apiStatus.className = "status-banner error"; apiStatus.innerHTML = ` <span></span> <span>您的浏览器不支持 Translator API 。请使用 Chrome 131+ 并启用相关实验性功能。</span> `; translateBtn.disabled = true; return; } // 2. 检查特定语言对是否可用 - 使用 availability() 方法 const translatorCapabilities = await self.Translator.availability({ sourceLanguage: 'zh', targetLanguage: 'en', }); if (translatorCapabilities === "unavailable") { apiStatus.className = "status-banner error"; apiStatus.innerHTML = ` <span></span> <span>Translator API 不可用</span> `; translateBtn.disabled = true; } else { apiStatus.className = "status-banner success"; apiStatus.innerHTML = ` <span></span> <span>Translator API 可用!可以开始使用翻译功能。</span> `; translateBtn.disabled = false; if (translatorCapabilities === "downloadable" || translatorCapabilities === "downloading") { apiStatus.innerHTML += `<div style="margin-top: 8px; font-size: 0.9em;"> 正在下载翻译模型...</div>`; } } } catch (error) { apiStatus.className = "status-banner error"; apiStatus.innerHTML = ` <span></span> <span>Translator API 不可用: ${error.message}</span> `; translateBtn.disabled = true; } } 这是最基础的功能,用户输入文本后点击按钮进行翻译。
<div class="translate-panel"> <!-- 源语言选择 --> <select id="sourceLang"> <option value="zh"> 中文</option> <option value="en"> 英语</option> <option value="ja"> 日语</option> </select> <!-- 输入框 --> <textarea id="inputText" placeholder="请输入要翻译的文本..."></textarea> <!-- 目标语言选择 --> <select id="targetLang"> <option value="en"> 英语</option> <option value="zh"> 中文</option> <option value="ja"> 日语</option> </select> <!-- 输出框 --> <textarea id="outputText" disabled placeholder="翻译结果..."></textarea> <!-- 翻译按钮 --> <button id="translateBtn" Onclick="translateText()"> 开始翻译</button> </div> async function translateText() { const text = document.getElementById('inputText').value.trim(); const source = document.getElementById('sourceLang').value; const target = document.getElementById('targetLang').value; const outputText = document.getElementById('outputText'); const translateBtn = document.getElementById('translateBtn'); // 输入验证 if (!text) { alert('请输入要翻译的文本'); return; } if (source === target) { alert('源语言和目标语言相同,无需翻译'); return; } try { // 禁用按钮,防止重复点击 translateBtn.disabled = true; translateBtn.textCOntent= ' 翻译中...'; outputText.value = ''; // 创建翻译器 const translator = await self.Translator.create({ sourceLanguage: source, targetLanguage: target }); // 执行翻译 const result = await translator.translate(text); // 显示结果 outputText.value = result; // 可选:销毁翻译器释放资源 // translator.destroy(); } catch (error) { console.error('翻译失败:', error); alert(`翻译失败: ${error.message}`); outputText.value = ''; } finally { // 恢复按钮状态 translateBtn.disabled = false; translateBtn.textCOntent= ' 开始翻译'; } } async/awaittry-catch 捕获翻译失败的情况finally 确保按钮状态恢复允许用户一键交换源语言和目标语言,并同时交换输入输出文本。
function swapLanguages() { const sourceLang = document.getElementById('sourceLang'); const targetLang = document.getElementById('targetLang'); const inputText = document.getElementById('inputText'); const outputText = document.getElementById('outputText'); // 交换语言选择 const tempLang = sourceLang.value; sourceLang.value = targetLang.value; targetLang.value = tempLang; // 交换文本内容 const tempText = inputText.value; inputText.value = outputText.value; outputText.value = ''; // 清空输出,等待新的翻译 } function clearText() { document.getElementById('inputText').value = ''; document.getElementById('outputText').value = ''; } 这是 Demo 的核心亮点之一 - 使用 Translator API 实现整个页面的自动翻译。
data-i18n 属性<h1 data-i18n="true"> Chrome 内置 AI 翻译器</h1> <p data-i18n="true">使用浏览器内置的 Translator API 进行实时翻译</p> <button data-i18n="true"> 开始翻译</button> <textarea placeholder="请输入文本..." data-i18n="true"></textarea> 重要说明:
data-i18n="true" 标记需要翻译的元素input 和 textarea,会翻译 placeholder 属性// 当前页面语言 let currentLang = "zh"; // 存储元素的原始文本(用于恢复) const originalTexts = new Map(); // 翻译器缓存(避免重复创建) const pageTranslators = {}; /** * 切换页面语言 * @param {string} targetLang - 目标语言代码 */ async function switchLanguage(targetLang) { // 更新按钮状态 - 添加加载动画 const targetBtn = document.querySelector(`.lang-btn[data-lang="${targetLang}"]`); document.querySelectorAll(".lang-btn").forEach((btn) => { btn.classList.remove("active", "translating"); }); targetBtn.classList.add("active", "translating"); try { // 翻译页面内容 await translatePage(currentLang, targetLang); // 更新当前语言 currentLang = targetLang; } catch (error) { console.error('切换语言失败:', error); alert(`切换语言失败: ${error.message}`); } finally { // 移除加载动画 targetBtn.classList.remove("translating"); } } /** * 翻译页面中所有标记为 data-i18n="true" 的元素 * @param {string} sourceLang - 源语言 * @param {string} targetLang - 目标语言 */ async function translatePage(sourceLang, targetLang) { // 如果源语言和目标语言相同,无需翻译 if (sourceLang === targetLang) { return; } try { console.log(` 开始翻译页面: ${sourceLang} → ${targetLang}`); // 获取或创建翻译器 const translatorKey = `${sourceLang}-${targetLang}`; if (!pageTranslators[translatorKey]) { console.log(` 创建翻译器: ${sourceLang} → ${targetLang}`); pageTranslators[translatorKey] = await self.Translator.create({ sourceLanguage: sourceLang, targetLanguage: targetLang }); } const translator = pageTranslators[translatorKey]; // 收集所有需要翻译的元素 const elements = document.querySelectorAll('[data-i18n="true"]'); if (elements.length === 0) { console.log(' 没有找到需要翻译的元素 (data-i18n="true")'); return; } console.log(` 找到 ${elements.length} 个需要翻译的元素`); // 遍历每个元素进行翻译 for (const el of elements) { // 跳过特定元素(如翻译功能区的输入框) if (el.id === 'inputText' || el.id === 'outputText') { continue; } // 添加翻译中样式 el.classList.add('translating-text'); try { // 保存原始文本(首次翻译时) if (!originalTexts.has(el)) { if (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA') { originalTexts.set(el, { placeholder: el.placeholder, value: el.value }); } else { // 提取纯文本内容(排除子元素) const textCOntent= getTextContent(el); originalTexts.set(el, { textContent: textContent }); } } // 获取当前文本内容 let textToTranslate = ''; if (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA') { textToTranslate = el.placeholder; } else { textToTranslate = getTextContent(el); } // 执行翻译 if (textToTranslate) { const translated = await translator.translate(textToTranslate); // 更新元素内容 if (el.tagName == 'INPUT' || el.tagName === 'TEXTAREA') { el.placeholder = translated; } else { // 只替换文本节点,保留子元素 replaceTextContent(el, translated); } } } catch (error) { console.error(`翻译元素失败:`, el, error); } finally { // 移除翻译中样式 el.classList.remove('translating-text'); } } console.log(` 页面翻译完成!`); } catch (error) { console.error(' 页面翻译失败:', error); // 移除所有 loading 类 document.querySelectorAll('.translating-text').forEach(el => { el.classList.remove('translating-text'); }); throw error; } } /** * 提取元素的纯文本内容(只包含直接文本节点,不包含子元素) */ function getTextContent(el) { let text = ''; for (const node of el.childNodes) { if (node.nodeType === Node.TEXT_NODE) { text += node.textContent; } } return text.trim(); } /** * 替换元素的文本节点内容,保留子元素 */ function replaceTextContent(el, newText) { // 如果元素没有子元素,直接替换 textContent if (el.children.length === 0) { el.textCOntent= newText; return; } // 如果有子元素(如链接),只替换文本节点 for (const node of el.childNodes) { if (node.nodeType === Node.TEXT_NODE) { node.textCOntent= newText; break; // 只替换第一个文本节点 } } } Translator API 的优势
实现整页翻译的关键步骤
data-i18n="true" 标记可翻译元素API 核心方法
// 检查可用性 const availability = await self.Translator.availability({ sourceLanguage, targetLanguage }); // 创建翻译器 const translator = await self.Translator.create({ sourceLanguage, targetLanguage }); // 执行翻译 const result = await translator.translate(text); 当前限制
Chrome 团队正在积极推进浏览器内置 AI 能力的标准化,未来可能会:
感谢 Chrome 团队为 Web 开发者带来了如此强大的本地 AI 能力!
1 ohoh 20 天前 你是个好人。 收藏了 |
2 SurgaOrange 20 天前 等一个本地离线的 AI 翻译插件 |
3 uhohoo 20 天前 好东西 |
4 june4 20 天前 真的好快,这底层用的啥模型,可以扣出来在外面用吗 |
5 xiaowoli OP @SurgaOrange GET 后面我实现一下 |
6 miraku 20 天前 ![]() 整页翻译有点慢 ```Javascript // ==UserScript== // @name 网页翻译 + 划词翻译气泡( Translator API ) // @namespace http://tampermonkey.net/ // @version 0.3 // @description 使用浏览器内置 Translator API 翻译网页或选中文本,并缓存语言设置(默认 英→中) // @author mirakyux // @match *://*/* // @grant none // @run-at document_idle // ==/UserScript== (async function() { 'use strict'; // ======== 配置与缓存 ======== const cacheKey = 'translator_langs'; const saved = JSON.parse(localStorage.getItem(cacheKey) || '{}'); let sourceLang = saved.sourceLang || 'en'; let targetLang = saved.targetLang || 'zh'; function saveLang() { localStorage.setItem(cacheKey, JSON.stringify({ sourceLang, targetLang })); } // ======== 样式 ======== function style(el, css) { Object.assign(el.style, css); } // ======== 悬浮按钮(整页) ======== const pageBtn = document.createElement('button'); pageBtn.textCOntent= ' 翻译网页'; style(pageBtn, { position: 'fixed', bottom: '20px', right: '20px', zIndex: 9999, padding: '10px 16px', backgroundColor: '#007bff', color: '#fff', border: 'none', borderRadius: '8px', cursor: 'pointer', boxShadow: '0 2px 6px rgba(0,0,0,0.3)', fontSize: '14px', opacity: '0.85', transition: 'opacity 0.3s' }); pageBtn.Onmouseenter= () => (pageBtn.style.opacity = '1'); pageBtn.Onmouseleave= () => (pageBtn.style.opacity = '0.85'); document.body.appendChild(pageBtn); // ======== 检测 Translator API ======== async function isTranslatorAvailable() { if (!('Translator' in self)) { console.error(' 当前浏览器不支持 Translator API 。请启用 chrome://flags/#translation-api'); return false; } try { const avail = await self.Translator.availability({ sourceLanguage: sourceLang, targetLanguage: targetLang }); if (avail === 'unavailable') { console.error(` 不支持语言对 ${sourceLang} → ${targetLang}`); return false; } console.log(` Translator API 可用 (${avail})`); return true; } catch (err) { console.error(' Translator API 检测失败:', err); return false; } } async function createTranslator() { return await self.Translator.create({ sourceLanguage: sourceLang, targetLanguage: targetLang }); } // ======== 整页翻译逻辑 ======== async function translatePage() { const available = await isTranslatorAvailable(); if (!available) { alert('当前浏览器不支持内置翻译 API ,请查看控制台提示。'); return; } pageBtn.disabled = true; pageBtn.textCOntent= '翻译中…'; const translator = await createTranslator(); const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, { acceptNode: (node) => { if (!node.nodeValue.trim()) return NodeFilter.FILTER_REJECT; if (node.parentElement && ['SCRIPT', 'STYLE', 'NOSCRIPT'].includes(node.parentElement.tagName)) return NodeFilter.FILTER_REJECT; return NodeFilter.FILTER_ACCEPT; } }); const textNodes = []; while (walker.nextNode()) textNodes.push(walker.currentNode); for (const node of textNodes) { const text = node.nodeValue.trim(); if (!text || text.length > 2000) continue; try { const translated = await translator.translate(text); node.nodeValue = translated; } catch (err) { console.warn('跳过节点:', err); } } pageBtn.textCOntent= ' 已翻译'; setTimeout(() => { pageBtn.textCOntent= ' 翻译网页'; pageBtn.disabled = false; }, 3000); } pageBtn.addEventListener('click', translatePage); // ======== 划词翻译气泡 ======== const bubble = document.createElement('div'); style(bubble, { position: 'absolute', background: '#007bff', color: '#fff', padding: '6px 10px', borderRadius: '6px', fontSize: '13px', cursor: 'pointer', display: 'none', zIndex: 99999, userSelect: 'none', whiteSpace: 'nowrap', boxShadow: '0 2px 6px rgba(0,0,0,0.3)' }); bubble.textCOntent= '翻译'; document.body.appendChild(bubble); let currentSelection = ''; document.addEventListener('selectionchange', () => { const sel = window.getSelection(); const text = sel.toString().trim(); if (text.length > 0) { const range = sel.getRangeAt(0); const rect = range.getBoundingClientRect(); bubble.style.left = `${rect.right + window.scrollX + 10}px`; bubble.style.top = `${rect.top + window.scrollY - 10}px`; bubble.style.display = 'block'; currentSelection = text; } else { bubble.style.display = 'none'; } }); bubble.addEventListener('click', async () => { if (!currentSelection) return; const available = await isTranslatorAvailable(); if (!available) return alert('当前浏览器不支持 Translator API'); const translator = await createTranslator(); bubble.textCOntent= '翻译中…'; try { const translated = await translator.translate(currentSelection); alert(` [${sourceLang} → ${targetLang}] \n\n${translated}`); } catch (e) { alert('翻译失败:' + e.message); } finally { bubble.textCOntent= '翻译'; bubble.style.display = 'none'; } }); // ======== 设置按钮 ======== const cOnfigBtn= document.createElement('button'); configBtn.textCOntent= ' 设置'; style(configBtn, { position: 'fixed', bottom: '60px', right: '20px', zIndex: 9999, padding: '8px 12px', backgroundColor: '#28a745', color: '#fff', border: 'none', borderRadius: '8px', cursor: 'pointer', fontSize: '13px', opacity: '0.85' }); configBtn.Onmouseenter= () => (configBtn.style.opacity = '1'); configBtn.Onmouseleave= () => (configBtn.style.pacity = '0.85'); document.body.appendChild(configBtn); configBtn.addEventListener('click', async () => { const src = prompt('源语言(如 en, zh, ja ):', sourceLang); const tgt = prompt('目标语言(如 zh, en, ko ):', targetLang); if (src && tgt) { sourceLang = src; targetLang = tgt; saveLang(); alert(`语言设置已保存:${sourceLang} → ${targetLang}`); } }); })(); ``` |
7 miraku 20 天前 |
8 newtype0092 20 天前 文章读到最后总感觉缺点什么,想了想发现是没有 References ... |
9 junkk 19 天前 试了下,并没有想象中的快,相比其他的应该也算可以了 |