- 前端
- AI 应用
- 本地部署
【免费下载链接】FluentRead
An open-source browser extension for bilingual translation. 一款开源的浏览器双语翻译插件。
FluentRead 是一款开源的浏览器双语翻译插件,其图片翻译、圈选翻译(本地 OCR 模式)等能力依赖 public/fluent-read-ocr/NOTICE.md 所声明的 Tesseract.js OCR 资产。本文以该 NOTICE 文档为核心,结合扩展内真实源码与测试,系统讲解 Tesseract.js 6.0.1 及其 core 6.1.2 如何被本地化打包、经 Offscreen 文档与 Web Worker 加载执行,以及 OCR 语言包(eng / chi_sim / jpn 等)如何按需下载、缓存与清除——帮助读者理解这套"零 CDN 运行时依赖"的本地文字识别基础设施的原理与工程实现。
一、NOTICE 文档声明了什么:扩展内置的三类 OCR 资产
public/fluent-read-ocr/NOTICE.md全文虽短,却精确界定了 FluentRead 内置 OCR 资产的组成与来源:
- Tesseract.js 6.0.1(Apache-2.0 许可):面向浏览器的 Tesseract 封装库,提供
createWorker等高层 API; - tesseract.js-core 6.1.2(Apache-2.0 许可):Tesseract 的 WebAssembly 内核;
- Tesseract 语言数据包(eng、chi_sim、jpn,MIT 许可):
traineddata识别模型数据。
NOTICE 同时强调了一个关键的工程决策:
The worker, WebAssembly files, and language data are loaded from this extension's own resources. No OCR code is downloaded from a third-party CDN at runtime.
即Worker 脚本、WebAssembly 文件和语言数据一律从扩展自身资源加载,运行时绝不从第三方 CDN 下载任何 OCR 代码。这句话决定了整个 OCR 模块的架构取向,也解释了public/fluent-read-ocr/目录为何只包含两个文件:
- core/tesseract-core-simd-lstm.wasm.js:SIMD + LSTM 版本的 WASM 内核(含 asm 化的 .wasm 二进制);
- worker/worker.min.js:Tesseract.js 的 worker 脚本。
注意:目录下只有 SIMD + LSTM 一种 core 变体。其原因在 ocrRuntime.ts 的注释中说明——扩展支持的 Chrome/Edge(Offscreen 需 109+)与 Firefox 140+ 均支持 WASM SIMD,且引擎只使用 LSTM 识别引擎,因此不再打包永远不会被选中的非 SIMD 变体,缩小扩展体积。
二、本地资源如何被定位:chrome.runtime.getURL 与扩展资产路径
Worker 与 core 之所以能以"扩展自身资源"身份加载,关键在于 ocrRuntime.ts 中的extensionAsset辅助函数:
function extensionAsset(path: string): string { const getRuntimeUrl = chrome.runtime.getURL as (assetPath: string) => string; return getRuntimeUrl(`/fluent-read-ocr/${path}`); }它以chrome.runtime.getURL把public/fluent-read-ocr/下的相对路径解析为chrome-extension://<extension-id>/fluent-read-ocr/...形式的内置 URL,随后传入 Tesseract.js 的createWorker配置:
createWorker(languages.split('+'), 1, { workerPath: extensionAsset('worker/worker.min.js'), // 本地 Worker 脚本 corePath: extensionAsset('core/tesseract-core-simd-lstm.wasm.js'), // 本地 WASM 内核 cachePath: 'fluent-read-image-ocr', workerBlobURL: false, // 不使用 Blob Worker logger: message => { if (message.status === 'recognizing text') onProgress(message.progress, message.userJobId); }, }, OCR_INIT_CONFIG)几个配置项的含义与工程动机:
workerPath/corePath:强制指向扩展内部资源,替代 Tesseract.js 默认的 CDN 地址,从根上兑现 NOTICE 的"零运行时 CDN"承诺;workerBlobURL: false:Tesseract.js 默认会把 worker 包装成 Blob URL,而 Offscreen 页面拥有扩展源,直接加载本地 worker 可规避 Blob Worker 的 CSP/源限制;cachePath: 'fluent-read-image-ocr':指定语言包在 IndexedDB 中的缓存键前缀;OCR_INIT_CONFIG = {tessedit_load_sublangs: ''}:关闭隐式子语言加载。这是针对 6.1.2 内置 core 的一个已知缺陷规避——该版本在遍历语言 vector 时会追加 CJK 模型声明的竖排子语言,导致迭代器失效并误读为「;」;由于所需语言已由getOcrLanguages显式传入,初始化时关闭隐式子语言即可保留已下载的主模型(上游同类问题见 tesseract-ocr/tesseract#4002)。
测试 imageOcrRuntime.test.ts 直接验证了这条资源链路:mock 的chrome.runtime.getURL把路径解析为chrome-extension://test/fluent-read-ocr/worker/worker.min.js与.../core/tesseract-core-simd-lstm.wasm.js,并断言createWorker以['jpn', 'eng']、1(单语言模型)和上述配置被调用。
三、识别在哪里跑:Offscreen Document + Web Worker 的隔离架构
本地 OCR 并不在页面上下文执行,而是运行在扩展的隔离环境。从 docs/architecture.md 可以看到整体运行时布局:Chrome/Edge MV3 使用原生 Offscreen Document,Firefox MV2 使用后台页面中的隐藏扩展 iframe,两者加载同一个offscreen.html,复用同一份 OCR、图片/区域绘制等逻辑。
识别调用链大致如下:
- 内容脚本或后台发起图片识别请求,经由 offscreenAdapter.ts 适配为平台 Offscreen 消息;
- offscreenRuntime.ts 在具备 Canvas/DOM 的隔离文档中完成图片解码、尺寸校验与调用编排;
- ocrRuntime.ts 创建 Tesseract.js Worker,真正的识别循环跑在
worker.min.js启动的 Web Worker 中; - ocrWorkerRuntime.ts 负责与引擎无关的 Worker 生命周期与串行任务队列。
Offscreen 页面与后台/内容脚本之间只通过类型化消息通信,识别产生的位图、Canvas 等临时资源在完成或失败后即被释放,见 offscreenRuntime.ts 的模块边界说明。
3.1 Worker 复用与串行队列:识别不会互相"踩踏"
ocrWorkerRuntime.ts 是整套并发控制的实现核心,它保证了:
- 同语言 Worker 复用:连续识别同语言图片时不会重复创建 Worker、重复初始化语言模型(
getWorker会比对workerLanguages); - 串行执行:所有操作挂在一条"尾链"(
operationTail)上排队,避免一次请求的setParameters/recognize被另一次请求交叉覆盖; - 语言切换先终止旧 Worker:等待正在进行的识别结束后才
terminate旧 Worker 并创建新语言 Worker,切换过程还带"所有权代际"(workerOwnershipGeneration)校验,防止旧切换恢复后误覆盖新请求安装的实例; - 任务隔离的进度上报:通过
jobId关联进度,只发布当前任务的真实百分比,丢弃非法(NaN/Infinity)、倒退、重复和迟到的进度值。
识别时会对 Worker 设置两个 Tesseract 参数:
worker.setParameters({ tessedit_pageseg_mode: pageSegmentationMode, // 默认 PSM.SPARSE_TEXT(11) preserve_interword_spaces: '1', })tessedit_pageseg_mode:页面分割模式。普通图片使用PSM.SPARSE_TEXT(稀疏文本),圈选翻译在空结果时才以PSM.SINGLE_BLOCK(6,单块)重试一次,见 ocrRuntime.ts;preserve_interword_spaces: '1':保留词间空格,避免识别文本粘连。
以上语义均有 ocrWorkerRuntime.test.ts 的测试覆盖,例如"复用同语言 Worker 和稀疏文本参数,连续识别不重复跨 Worker 初始化"(第 69-84 行)、"等待正在进行的识别结束后才终止 Worker 并切换语言"(第 86-107 行)。
3.2 取消语义:排队取消与执行中取消
取消(AbortSignal)被区分为两种情形,ocrWorkerRuntime.ts 对此有精细处理:
- 排队中的请求被取消:只结束自己的等待,不阻塞后续任务,尾链继续等待前一任务完成,避免后来的请求越过正在进行的 OCR;
- 执行中的请求被取消:立即释放当前 Worker(
terminateCurrentWorker),因为其内部状态已不可信,同时保证取消不会阻塞串行尾链。
两条路径都以AbortError(name: 'AbortError',提示"图片 OCR 请求已取消")拒绝调用方,且会接住底层迟到的 Promise 拒绝,避免遗留未处理 Promise。
四、识别前后的图像处理:解码、缩放、坐标还原与结果缓存
ocrRuntime.ts 在调用 Worker 前后还承担了完整的图像管线:
4.1 图片解码与预缩放
loadOcrImage负责把 data URL 解码为HTMLImageElement,并设置15 秒解码超时(OCR_IMAGE_DECODE_TIMEOUT_MS = 15_000),超时、数据无法解码、请求被取消都会给出明确错误并释放src。解码完成后立刻清空source.src释放像素,不等远端识别结束(第 143-147 行)。
prepareOcrImage则根据识别场景(image整图 /area圈选)把图片缩放/加边后绘制到 Canvas 再编码为 PNG:
- 整图识别直接使用
getOcrImageSize的目标尺寸; - 圈选识别使用
getAreaOcrImageSize,小图会有界放大并加白边(padding),以提升低分辨率选区的识别率; - 绘制使用高画质插值(
imageSmoothingQuality = 'high'),绘制/编码完成后立即将 Canvas 宽高置 0 释放像素,"不与 OCR WebAssembly 长期并存"(第 136-140 行)。
4.2 坐标映回与结果缓存
Tesseract 返回的是归一化/缩放后的 block 结构,restoreOcrLineCoordinates会把行级bbox依据sourceWidth/sourceHeight、目标尺寸与 padding 映回原图坐标,供翻译绘制层直接在原图上定位。
完成的结果会进入一个有界 LRU 缓存(completedRecognitionCache):
- 上限3 张图 / 12 MB(
MAX_CACHED_OCR_IMAGES = 3、MAX_CACHED_OCR_BYTES = 12 * 1024 * 1024),超出时淘汰最旧条目; - 返回给调用方的是深拷贝(
copyOcrLines),隔离调用方对缓存内容的修改; - 缓存命中不重放旧进度、不重复创建 Image/Canvas;
- 清除语言包后缓存整体清空,保证重新识别(见 imageOcrRuntime.test.ts 与 第 32-41 行)。
五、语言包体系:八种语言、按需下载与 IndexedDB 缓存
NOTICE 声明的三个内置语言包(eng、chi_sim、jpn)只是"默认随扩展声明"的最小集合。实际的语言包体系由 ocrLanguages.ts 定义,共8 种可下载语言:
| 代码 | 语言 | 说明 | 预估大小 | 推荐 |
|---|---|---|---|---|
chi_sim | 简体中文 | 识别简体中文界面、截图和图片文字 | 约 20 MB | ✅ |
chi_tra | 繁體中文 | 识别繁体中文界面、截图和图片文字 | 约 20 MB | ✅ |
eng | English | 识别英文和拉丁字母文字 | 约 11 MB | ✅ |
spa | Español | 识别西班牙语图片文字 | 约 11 MB | ❌ |
jpn | 日本語 | 识别日文图片和漫画文字 | 约 16 MB | ✅ |
kor | 한국어 | 识别韩语图片文字 | 约 2 MB | ❌ |
fra | Français | 识别法语图片文字 | 约 1 MB | ❌ |
rus | Русский | 识别俄语图片文字 | 约 5 MB | ❌ |
(数据来自 ocrLanguages.ts 的IMAGE_OCR_LANGUAGE_PACKS,"约 XX MB" 为源码声明的下载体积,实际以网络传输为准。)
5.1 源语言 → 语言包的映射规则
getRequiredImageOcrLanguages 把用户选择的源语言映射为需要加载的 Tesseract 语言代码:
- 中文按脚本(
getChineseScript)细分:简体(Hans)→chi_sim + eng,繁体(Hant)→chi_tra + eng; - 明确的英/日/西/韩/法/俄语言 → 对应代码(英文只加载
eng,其余语言加载[代码, 'eng'],英文作为拉丁兜底); - 自动源语言→ 加载推荐组合
['chi_sim', 'chi_tra', 'eng', 'jpn'],避免默认配置把繁体识别成简体后丢失脚本信息。
5.2 语言包下载、缓存与清除
语言包不随扩展打包(这与 NOTICE 声明的"语言数据从扩展自身资源加载"并不冲突——NOTICE 针对的是扩展内core与worker两个文件,而 traineddata 本身过大且可裁剪,因此按需下载)。下载与缓存流程在 ocrRuntime.ts 注释中有清晰说明:
不再把 traineddata 打进扩展;Tesseract.js 会从 jsDelivr 按需下载,并将解压后的语言包缓存到 Offscreen Document 的 IndexedDB。
- 下载入口:
downloadImageOcrLanguages→ocrWorkerRuntime.ensureLanguages,通过getWorker(normalized)触发 Tesseract.js 的语言包获取流程,支持AbortSignal取消;客户端侧的语言包准备还有 300 秒超时保护(见 client.ts); - 缓存位置:IndexedDB 的
keyval-store/keyval仓库(Tesseract.js 内部 idb-keyval 的默认库),键形如fluent-read-image-ocr/<lang>.traineddata; - 清除入口:
removeImageOcrLanguages→clearModels→ ocrModelCache.ts 的removeOcrModelFiles,按cachePath精确删除指定语言的traineddata键,等待事务提交后返回,同时清空完成结果缓存;只删除明确指定的模型键,不清空数据库。
下载状态由后台 ocrLanguageRepository.ts 持久化,键为fluentReadImageOcrLanguages;用户也可以从图片翻译设置中的语言包卡片上手动"清除模型"释放磁盘占用。
六、隐私边界:识别本地完成,像素不上传
本地 OCR 在隐私上有一层重要承诺(见 docs/guide/privacy.md 与 docs/guide/image-translation.md):
文字识别在浏览器本地完成,识别出的文字交给所选翻译服务;图片像素不会作为文本翻译请求上传。
换言之,图片数据只在本机(Offscreen + Worker + WASM)中流转,识别出的文本才可能发送给用户配置的翻译服务;语言包首次下载需要网络,属于资源获取而非图片上传。圈选翻译还支持"优先使用模型识图":模型支持图片输入时直接把裁剪后的选区图片交给模型识别,这条路径不加载 OCR 语言包、不执行本地识别(见 offscreenRuntime.ts)。
七、WASM 诊断与打包验证
core/tesseract-core-simd-lstm.wasm.js这类 vendor WASM 在生产中常产生难以排查的 stderr 输出,FluentRead 用 scripts/wasm/diagnostics.js 对内核的打印逻辑做了插桩适配,并由 wasmDiagnostics.test.ts 守护:
- 将引擎的
printErr重定向到扩展的日志通道(测试断言插桩后代码包含n=b.printErr||fluentReadWasmStderr,见 第 63-64 行); - 保留原始严重级别、去除终端颜色,把"已知无害"的 LSTM 旧参数警告(如
language_model_ngram_on、chop_enable等)和分辨率估算提示降为 debug,而Error opening data file eng.traineddata、Aborted(out of memory)、Error loading model等真实错误保持可见(第 27-48 行)。
这套诊断管道让内置 WASM 的报错既能被开发/用户看到,又不会让已知噪音干扰日志,是"扩展自带 WASM 资产"工程化的一个典型配套。
八、小结:从 NOTICE 到可运行本地 OCR 的完整链路
public/fluent-read-ocr/NOTICE.md虽只有寥寥几行,但它所描述的三类资产(Tesseract.js 6.0.1、tesseract.js-core 6.1.2、traineddata 语言包)和"零运行时 CDN"原则,在源码中被落实为一套完整链路:
- 资源定位:
chrome.runtime.getURL指向扩展内置的worker.min.js与tesseract-core-simd-lstm.wasm.js(ocrRuntime.ts); - 隔离执行:Offscreen Document + Web Worker 承载识别,串行队列保证并发安全(ocrWorkerRuntime.ts);
- 图像管线:解码超时、缩放加边、坐标还原、有界结果缓存(ocrRuntime.ts);
- 语言包管理:8 种语言按需下载至 IndexedDB,支持定向清除(ocrLanguages.ts、ocrModelCache.ts);
- 隐私与可观测:识别留在本地、像素不上传,WASM stderr 经插桩分级输出。
相关代码路径汇总:NOTICE 声明、OCR 运行时、Worker 运行时、语言包目录、语言包缓存清除、Offscreen 编排、运行时测试、OCR 运行时测试、WASM 诊断测试。读者可按上述路径逐一深入,或直接参考 图片翻译指南 体验该能力的实际使用方式。
- 前端
- AI 应用
- 本地部署
【免费下载链接】FluentRead
An open-source browser extension for bilingual translation. 一款开源的浏览器双语翻译插件。
相关推荐
FluentRead 图片翻译完整指南:本地 OCR 识别、语言包管理、悬浮入口与隐私边界
FluentRead 图片翻译完整指南:本地 OCR 识别、语言包管理、悬浮入口与隐私边界 本文以 FluentRead 开源浏览器双语翻译插件的图片翻译功能为
前端AI 应用本地部署昇腾GNN算子库ops-gnn:一文读懂10大核心算子如何加速图神经网络
昇腾GNN算子库ops gnn:一文读懂10大核心算子如何加速图神经网络 ops gnn 是昇腾生态下图神经网络(GNN)专用算子库 ,基于 Ascend C
算子库人工智能深度学习Ascend3个核心技巧彻底解决微信QQ消息撤回烦恼:RevokeMsgPatcher实战指南
3个核心技巧彻底解决微信QQ消息撤回烦恼:RevokeMsgPatcher实战指南 你是否曾因错过重要消息而感到懊恼?当对方撤回消息时,你是否好奇那条消失的内容
桌面应用即时通讯
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考