# Documents -> translatable HTML (browser extension) A Chromium MV3 extension that opens **PDF, EPUB, MOBI, AZW3, FB2, RTF, TXT, Markdown, local HTML and CBZ/CBT comics** and re-renders them as clean, semantic HTML so the browser's built-in **Translate page** works on them for free (no API key, no invoice, no catch). Spin-off of [doc-html-translate](../README.md); it ports that app's format extractors to run in-browser - PDF.js for PDF, a native unzip + DOM pipeline for EPUB, hand-ported readers for TXT/RTF/FB2/HTML, the vendored `marked` for Markdown, and the vendored `foliate-js` for MOBI/AZW3 (replacing the desktop's Calibre dependency). Comic archives open page by page with each page's speech-bubble text recognized (OCR) and overlaid as translatable text; **CBR and CB7 are desktop-app only** - a browser has no RAR/7z decoder, so those show a "use the desktop app" notice. It also does **image OCR**: right-click a picture ("OCR & translate this image"), open a standalone image file from the popup, or turn on "Use OCR for images" for PDFs/EPUBs - the extension recognizes the text baked into the picture and lays it over the image as real text, so the browser's **Translate page** reaches that too. English is bundled; more languages download on demand. **Available now on the [Chrome Web Store](https://chromewebstore.google.com/detail/nmcckamdocainafmmompkbmelkpbnmic)** (Chrome and Edge / Chromium). An Edge Add-ons listing is still planned; you can also load it unpacked from this folder (see below). This extension is one of several forms of the same project - the desktop CLI/GUI, the Microsoft Store app, and this extension; see [Editions](../README.md#editions). The make-or-break decision for PDF (see [the spec](../DEV/research/pdf_translate_extension_spec.md)): reflow to flowing `
` / `
/ interface strings for 13 languages (Chrome names the Chinese directory zh_CN)
build.mjs `stamp` (date-time version), `vendor` (sync pdfjs) and `zip` (store package)
```
## Develop
```sh
npm install # pulls pdfjs-dist + tesseract.js / tesseract.js-core (dev dependencies)
npm run vendor # copies pdfjs + the Tesseract engine and eng.traineddata into vendor/ (run once after install)
npm run stamp # sets manifest.json + package.json version to the date-time (yy.MMdd.HHmm) - run before reloading unpacked to see which build you're testing
npm test # unit-tests the pure modules (no browser needed)
npm run build # stamp + vendor + zip -> dist/ (store-ready, auto-versioned)
npm run zip # only zip (uses the current version)
```
`vendor/` is generated and git-ignored; run `npm run vendor` before loading the extension. The version
is a **date-time stamp** (`yy.MMdd.HHmm`, same scheme as the desktop `build.ps1`) - it shows in the popup
and options footer and in `chrome://extensions`, so you can tell which build is loaded. `npm run build`
re-stamps automatically; for local unpacked testing run `npm run stamp` then reload the extension.
## Load unpacked (Chrome / Edge)
1. Go to `chrome://extensions` (or `edge://extensions`), enable **Developer mode**.
2. **Load unpacked** -> select this `extension/` folder.
3. For local PDFs / EPUBs you have two options:
- **Easiest / most robust:** open `chrome-extension:///src/viewer.html` and click **Open file**
to pick a PDF or EPUB from the OS dialog. This needs no toggle and no host access, and - because the
file path never appears in any URL - it is immune to URL-based content blockers (a common cause of an
`ERR_BLOCKED_BY_CLIENT` on the viewer page).
- **Auto-intercept:** open the extension's **Details** and turn on **Allow access to file URLs**, then
open a `file://...pdf` or `file://...epub` directly and it redirects to the viewer.
## Manual acceptance (the spec's gates - need a real browser)
The pure heuristics are covered by `npm test`. The end-to-end gates are manual:
- **Step 1 (PoC gate).** Open `chrome-extension:///src/viewer.html?file=` directly on a few
text PDFs. Run **Translate page**. The whole document - including text that was off-screen at load -
must translate with no doubled, overlapping, or scaled text. This de-risks the whole project.
- **Step 2 (interception).** Open any web or local `*.pdf` or `*.epub`; the viewer should auto-load.
Toggle the extension off in the popup and confirm the native PDF viewer / EPUB download returns. Use
**Original PDF** in the toolbar to open the untouched PDF in the native viewer without turning the
extension off (EPUB has no native viewer, so that button is hidden for EPUBs).
- **Step 3 (reflow quality).** Paragraphs, headings, and the TOC should read comparably to the desktop
app on the same PDFs.
- **Step 4 (reading UX).** Change font size/family/theme, jump pages, watch progress on a large PDF.
- **Step 5 (hardening).** Large, corrupt, password-protected, and scanned PDFs each give a correct
result or a clear fallback (no crashes/hangs). Scanned PDFs show a "little or no text" notice that
points back to the desktop OCR flow.
- **Step 6 (chunked rendering).** On a PDF of more than `PAGE_CHUNK` (50) pages: the status bar
settles on "Pages 1-50 of N" instead of rendering the whole book, and scrolling to page 48 loads
the next 50 without a jump or a flicker. Then the part that pays for the whole design: run
**Translate page** while sitting on page 10, scroll past 48, and confirm the newly appended pages
arrive translated and the existing translation survives. Jump to a far page via the page box and
the TOC - both must land on real text, not an empty section. Finally "↓ HTML" must save the
pages reached and say so in the status bar.
## Translate text in images (OCR)
Text baked into images (scanned pages, comics, screenshots) is invisible to the browser translator. The
extension can OCR it **on-device** (Tesseract, bundled) and lay the recognized text over the image as
real, translatable HTML, so one **Translate page** covers pictures too. Ways in:
- **Any web image:** right-click -> **OCR & translate this image**. Opens a new tab, OCRs the image,
overlays the text, and sets `` so the browser offers Translate page.
- **A local image file:** click **Open file** in the viewer (or *Open a document..* in the popup) and pick
a PNG / JPEG / GIF / BMP / WebP. It is OCR'd on open regardless of the *Use OCR for images* toggle -
opening a bare image is itself the request to read its text. There is deliberately **no** file-type
association for images (unlike PDF/EPUB), so opening an image URL never hijacks it - only the explicit
Open file / right-click paths OCR images.
- **PDFs / EPUBs:** turn on **Use OCR for images** (in the toolbar popup, next to *Open file*, or in
Options). Images are OCR'd lazily as they scroll into view; an "OCR: done/total" counter shows progress
so a picture-heavy document never looks frozen.
**English is bundled** and works fully offline. Other languages (Russian, Ukrainian, Japanese, ..) are
optional: they download on demand from the tessdata_fast host and cache locally for reuse - the only new
network access, and only when you click **Download**. See [`store/PRIVACY.md`](store/PRIVACY.md).
## Known limitations
- Interception matches the **request URL** (`*.pdf` / `*.epub`), so files served without a matching
extension in the URL aren't auto-intercepted (open them via **Open file** in the viewer). DNR can't
see the response Content-Type before the request.
- PDF.js text extraction is weaker than Poppler on ligatures / non-standard font maps (same caveat the
desktop app notes for its pure-Go reader - exotic fonts remain a humbling experience).
- Scanned/image-only PDFs are out of scope (OCR); the viewer detects them and offers the original.
- EPUB: the viewer applies its own clean reading CSS and drops author stylesheets/inline styles, so
heavily designed books lose their exact layout (by design - that is what makes them translate cleanly).
ZIP64 archives are not supported (vanishingly rare for EPUB). DRM-protected EPUBs cannot be read.
- MOBI/AZW3 are parsed in-browser by the vendored `foliate-js` (no Calibre needed); DRM-protected Kindle
files cannot be read (same as the desktop app). TXT/RTF/Markdown/FB2/HTML are opened via the file picker
(or, for FB2/RTF, a `*.fb2` / `*.rtf` URL); plain `.txt`/`.html` pages are left to the browser.
- Comics: **CBZ (zip) and CBT (tar) only**. Pages load and OCR lazily as you scroll (OCR is forced on, since
a comic has no text layer); page order is the archive's natural filename order. **CBR (RAR) and CB7 (7z)
are declined** with a "use the desktop app" notice - a browser cannot decode RAR/7z. ZIP64 CBZs are not
supported (same as EPUB).
- Firefox and mobile are out of scope (different PDF + interception story).
- With OCR **off**, the viewer is text-extraction only (`getTextContent()`) and never rasterizes pages.
OCR (Tesseract) uses WebAssembly, and scanned-PDF OCR rasterizes the page, so the manifest sets
`"content_security_policy": { "extension_pages": "script-src 'self' 'wasm-unsafe-eval'; object-src 'self'" }`
(`minimum_chrome_version` is 105, comfortably >= 103).
- OCR accuracy depends on image quality and the chosen language; stylized/vertical text may recognize
imperfectly. Text is grouped by the engine's blocks, not by detected speech bubbles.
## Store packaging & publishing
`npm run zip` produces a minimal-permission, store-ready ZIP. The extension is **published on the
[Chrome Web Store](https://chromewebstore.google.com/detail/nmcckamdocainafmmompkbmelkpbnmic)**; an Edge
Add-ons listing is still planned. Listing copy and review-form answers live in
[`store/LISTING.md`](store/LISTING.md), the hosted privacy policy in [`store/PRIVACY.md`](store/PRIVACY.md)
(served as `extension-privacy.html`), and the release/automation steps in [`PUBLISHING.md`](PUBLISHING.md).
Selling point for the listing: everything runs locally; only the browser's own translate touches the
network, and even that waits until you ask.