Browser extension that translates web pages, PDFs, and selected text, including content that keeps loading and updating after the page first renders. Works where built-in browser translation falls short.
Built-in browser translation (Chrome, Safari, Firefox) works well for many pages. But if you've run into any of these, TRANSLATE! is for you:
The page keeps changing and translation falls behind. Single-page apps, live feeds, chat threads, infinite scroll -- content that loads and updates after the page first renders. Google Translate and built-in translators usually translate once and miss whatever arrives later. TRANSLATE! watches the page and translates new content as it appears.
The translate bar never shows up. Browsers decide whether to offer translation based on the page's language tag. If the tag is missing, wrong, or matches your browser language, the translate bar silently doesn't appear -- even when the page is clearly in another language. TRANSLATE! lets you translate any page on demand, regardless of what the page declares.
You need to translate a PDF. Chrome's built-in translation does not work on PDFs. TRANSLATE! includes a PDF viewer that translates documents while preserving layout, and lets you save the translated version.
You use Safari or iOS. Safari has Apple's own translation (added in Safari 15 / macOS Monterey), but it supports fewer languages and isn't available on all pages. On iOS, options are even more limited. TRANSLATE! works across Chrome, Safari, Firefox, Edge, and Brave with the same feature set.
You work with less common languages. Translation quality for major pairs like English-Spanish or English-French is good across most services. For less common pairs -- Finnish-Thai, Estonian-Korean, Latvian-Vietnamese -- quality varies significantly between providers. TRANSLATE! lets you route through whichever supported path handles your language pair best, whether that's Chrome Built-in, OPUS-MT, DeepL, or a configured cloud provider.
Pages with mixed languages don't translate correctly. When a page contains text in multiple languages (e.g., a German forum on an English-tagged site), built-in translation often either skips it entirely or translates everything as if it were one language. TRANSLATE! detects language per text node using trigram analysis.
You want control over where your text goes. Built-in translation sends your page text to Google (Chrome), Apple (Safari), or Mozilla's servers (Firefox). TRANSLATE! lets you choose: use Chrome Built-in when available, use your own API keys with supported cloud providers, or run OPUS-MT locally in the browser via WebAssembly. Your text, your choice.
- Full-page translation -- translates visible text on a page, including dynamically loaded content and iframes. A MutationObserver watches for DOM changes so content added after page load is caught.
- PDF translation -- built-in PDF viewer with layout-preserving translation. Supports provider document APIs (Google Cloud, DeepL) and a local WASM pipeline. Save translated PDFs.
- 7 shipping translation providers -- Chrome Built-in, OPUS-MT, TranslateGemma (experimental, offscreen WebGPU/WebNN path in
src/offscreen/translategemma.ts), DeepL, OpenAI, Anthropic, and Google Cloud. NLLB-200 (src/providers/nllb-200.ts) is in tree as an opt-in research path. Switch on the fly between the available native, local, and cloud paths. - WebMCP integration -- shipped in PR #509, the content script exposes the extension as Model Context Protocol tools (
translate_page,translate_selection,detect_language) to any in-page MCP-aware agent viasrc/content/webmcp.ts. Compatible MCP clients (Claude.ai, agent harnesses) can drive translation directly without going through the popup. End-to-end harness lives ine2e/webmcp-harness.html+e2e/webmcp-integration.spec.ts. - Failover and load balancing -- if your primary provider hits a rate limit or fails, requests automatically route to the next provider in your chain.
- Smart batching and caching -- identical strings translated once and reused. Hidden elements skipped. Session cache minimizes repeat API calls.
- Auto-translate -- optionally translate pages on load.
- Source language auto-detection -- browser-native detectors first, with offline trigram/script fallback when needed.
- Screenshot translation -- drag a rectangle over any part of the page, and the extension captures that region, reads the text out of it with Tesseract.js (optical character recognition, running in the extension itself), translates it, and shows the result in an overlay under the selection. The overlay has a close button and disappears on its own after 30 seconds. It runs from the "Screenshot translate mode" command, which ships without a default key: assign one at
chrome://extensions/shortcuts. - Keyboard shortcuts --
Ctrl+Shift+Ptranslate page,Ctrl+Shift+Ttranslate selection,Ctrl+Shift+Uundo. - Diagnostics dashboard -- live usage metrics, cost tracking, latency histogram.
TRANSLATE! is not a drop-in replacement for built-in translation in every scenario:
- Setup required. Most providers need an API key. Built-in translation works with zero configuration.
- API costs. Cloud providers charge per character or token. Chrome Built-in and OPUS-MT avoid per-request API billing, but OPUS-MT usually has lower quality than the best cloud pairs.
- Not instant. Built-in translation is tightly integrated with the browser engine. TRANSLATE! works as a content script, which means a short delay on large pages.
If built-in translation works reliably for your languages and pages, you probably don't need this. TRANSLATE! is for the cases where it doesn't.
| Metric | Value |
|---|---|
| Unit tests | 6.5k+ Vitest cases across 160+ files |
| Coverage gates | Enforced in CI via npm run test:coverage |
| Contract tests | Provider interface conformance checks |
| Mutation testing | Stryker configured for core + providers |
| E2E tests | Playwright smoke, integration, and harness flows |
| CI | GitHub Actions: consolidated CI, smoke e2e, coverage, CodeQL, SBOM |
The unit suite runs in about a minute locally, depending on cache state. Coverage thresholds are enforced in CI via npm run test:coverage; see vitest.config.ts for the current gate values.
| Browser | Status | Notes |
|---|---|---|
| Chrome (116+) | Full support | Primary platform, Manifest V3 |
| Chromium-based (Edge, Brave, etc.) | Full support | Load as unpacked extension |
| Safari (macOS, iOS, iPadOS) | Full support | Built via Xcode converter |
| Firefox | Supported | Separate build flow documented below |
- Install dependencies
npm install
- Build the extension
npm run build
- Load in browser
- Open
chrome://extensions(or the equivalent page in your Chromium-based browser). - Enable Developer mode.
- Click Load unpacked and select the generated
dist/directory.
- Open
- The extension requests access to all websites so translations can run automatically. Allow the permission prompt when loading the extension.
If Chrome reports Service worker registration failed. Status code: 15, ensure
you selected the dist/ folder produced by the build step. Loading the repository
root or a directory missing manifest.json will cause the worker to fail.
Run the Safari converter on a Mac to produce an Xcode project for both macOS and iOS/iPadOS:
npm run build:safariOpen the generated project in Xcode to sign and build the extension for the desired platform.
See safari/README.md for detailed iOS/iPadOS deployment steps.
npx vite build --config vite.config.firefox.tsThis produces dist-firefox/, which you can load as a temporary extension in Firefox.
The repository does not currently expose a dedicated package:firefox npm script.
Remove the extension from the browser's extension management page.
Reload the unpacked extension after replacing the files with a newer version.
The repository includes a workflow that builds and signs a Chrome extension package.
- Open the Actions tab and run Sign Chrome Extension.
- The job builds the project, signs it using the
CRX_PRIVATE_KEYsecret, and uploadstranslate-extension.crxandtranslate-extension.zipas artifacts.
To sign locally:
npm run build
echo "$CRX_PRIVATE_KEY" | base64 -d > key.pem
npx -y crx pack dist -o translate-extension.crx --zip-output translate-extension.zip -p key.pemOpen the popup and click the gear button to access Settings. The settings page provides:
- General -- toggle automatic language detection and manage the glossary.
- Providers -- enable, disable, or reorder the shipped providers. Use Edit to supply API keys, models, and per-provider limits for the supported cloud providers.
- Advanced -- enable or clear the translation cache.
Use the Diagnostics button on the home page to view usage metrics and run connectivity checks.
| Provider | API Keys | Notes |
|---|---|---|
| OpenAI | platform.openai.com | GPT models |
| Claude (Anthropic) | console.anthropic.com | Claude models |
| DeepL | deepl.com/pro-api | Document translation |
| Google Cloud | cloud.google.com/translate | Translation + Detection |
Chrome Built-in, OPUS-MT, and TranslateGemma are shipped local/native paths and do not require API keys.
See also: docs/PROVIDERS.md
- Click the toolbar icon to open the popup.
- Press Translate page or use
Ctrl+Shift+P. - Enable Auto-translate to translate pages on load without clicking.
The extension translates visible text, watches for new content via MutationObserver, and caches translations for the session. Failed translations are queued and retried automatically. A status indicator appears in the bottom-right corner while work is in progress.
PDFs open in a built-in viewer. Two translation modes:
- Document API -- sends the file to Google Cloud or DeepL's document translation endpoint (if configured). Returns a fully translated PDF.
- Text extraction -- extracts text, translates via your configured providers, renders a new PDF locally. Works with any provider.
Translated PDFs can be saved from the viewer.
Requests are queued to stay within provider limits (default: 60 req/min, 100K tokens/min). The popup shows live usage bars that turn yellow/red as limits approach. If a provider returns 429, the request retries automatically or fails over to the next provider.
Cost tracking is built in -- the popup shows 24-hour and 7-day spend per provider.
Common issues (click to expand)
- Page not translating: Check source/target language settings. Enable Debug logging in the popup and check the browser console.
- Translate bar doesn't appear on some sites: This is a browser limitation, not a TRANSLATE! issue. Use
Ctrl+Shift+Por the popup button instead. - 401/403 errors: Invalid API key. Check provider config -- OpenAI uses
Bearer <key>, DeepL usesDeepL-Auth-Key <key>. - 429 rate limit: Requests retry automatically. Lower requests/tokens per minute in settings, or wait.
- CSP-restricted sites: Some sites block content script DOM changes. Translation may be limited.
- Frames: Same-origin iframes and open Shadow DOM are supported. Cross-origin frames require host permissions.
Use the Diagnostics panel (popup home page) for cache stats, connectivity checks, and a latency histogram. Copy Report generates a shareable summary for bug reports.
A command-line translator is included:
node cli/translate.js -k <API_KEY> -s <source_lang> -t <target_lang>Streams translations by default. Use --no-stream for batch mode, -d for debug output. See cli/translate.js --help.
npm install # Install dependencies
npm test # Run the Vitest suite
npm run test:e2e # Run the full web + harness E2E suite
npm run build # Chrome production build (dist/)
npm run build:safari # Safari via Xcode converterdist/ is not tracked in git. See docs/DEVELOPMENT.md for architecture details, provider internals, and the throttle/registry system.
Use Changesets for all updates. Documentation-only changes should use release type none.
A nightly workflow rebases open PRs to keep branches current with main. See AGENTS.md for details.
This project is licensed under the GPL-3.0-or-later.