A Chrome extension that translates web pages, PDF files, and Office documents using custom LLM backends (OpenAI, Anthropic Claude, Google Gemini, DeepSeek, Qwen, GLM, Ollama local models, etc.).
- Unified branding — extension name unified to Easy Translator across all locales (en/zh_CN/ja/ko), replacing inconsistent names like "Universal Translator" and "万能翻译器"
- Custom Prompt system — new Prompt Manager in Settings:
- Save multiple translation prompt templates, switch freely
{src}/{tgt}variables auto-replaced with source/target language- Batch translation automatically appends
[#N]segment marker instructions, compatible with custom prompts - Create new templates, update existing ones, or delete unused ones
- Translation abort mechanism —
AbortControllerintegration:- Stop button now sends
abortTranslationmessage to cancel all in-flight HTTP requests in the background - All LLM backends (OpenAI / Anthropic / Gemini / Ollama) pass the abort signal
- Returns
aborted: trueon cancellation to avoid false error reporting
- Stop button now sends
- Context menu stability fix — call
chrome.contextMenus.removeAll()before creating menu items on install, eliminatingduplicate iderrors
- Ollama local model support — supports local Ollama models, including custom models trained with
ollama train
- Full page translation — translates all text content on a web page while preserving DOM structure
- Selection translation — select text and press
Alt+T(orOption+Ton macOS) for instant translation - Right-click context menu — "Translate Selected Text" and "Translate Entire Page" options
- Display modes — floating tooltip, sidebar, or inline replacement
- Toggle original/translated — switch between original text and translations after page translation
- Stop button — cancel in-progress translation at any time
- Auto-recovery — if extension is updated/reloaded, prompts to refresh the page automatically
- PDF translation — opens PDFs in a custom viewer that renders pages as images with translation panels below each page, preserving exact layout
- Office files — basic text extraction for PPTX, DOCX files
- Manual viewer — PDF/Office files are no longer auto-redirected; open via popup button only
- Parallel translation — translates content in chunks using a concurrent worker pool (4 workers by default) for 4x speed improvement
- Batch translation — uses numbered segment markers (
[#N]) with LLM prompts for reliable batch processing - Content-based chunking — splits text into ~1000-character chunks following natural layout boundaries
- Multiple LLM backends — supports OpenAI-compatible APIs, Anthropic Claude, Google Gemini, and Ollama local models
- No API key required — connects to your local Ollama instance directly
- Auto model discovery — fetches installed model list from Ollama automatically
- CORS configuration — requires
OLLAMA_ORIGINS=*environment variable
- In-memory cache (content script) — same-session toggle support, instant
- Background service worker cache — persists across page refreshes within browser session, avoids re-translating the same URL
- Auto-detects browser language — UI switches between English, Chinese (zh_CN), Japanese (ja), and Korean (ko) based on Chrome's language setting
- All popup and viewer UI text is localized
- Language auto-save — switching source/target language in the popup immediately saves to storage, so selection translation respects the update without needing to click "Save Settings"
- API Key auto-hide — selecting Ollama as provider hides the API Key field
- Custom Prompt templates — save and switch between multiple translation prompt templates in Settings
┌─────────────────────────────────────────────────┐
│ Chrome Extension │
├─────────────────────────────────────────────────┤
│ popup/ │
│ ├── popup.html Quick translate + Settings │
│ ├── popup.js Settings logic, API calls │
│ └── popup.css │
├─────────────────────────────────────────────────┤
│ content/ │
│ ├── content.js Page translation engine │
│ │ - Text node collection & filtering │
│ │ - Parallel chunk translation (worker pool) │
│ │ - Selection translation (tooltip/sidebar) │
│ │ - Toggle original/translated text │
│ │ - Stop/abort mechanism │
│ ├── content.css Float button, tooltip, banner│
│ └── pdf-content.js (legacy, replaced by viewer)│
├─────────────────────────────────────────────────┤
│ viewer/ │
│ ├── viewer.html PDF/Office translation UI │
│ ├── viewer.js pdf.js renderer + translator │
│ │ - Page image rendering (canvas → bg image) │
│ │ - Paragraph grouping by spatial clustering │
│ │ - Translation panels below each page │
│ ├── viewer.css │
│ ├── pdf.min.js (bundled pdf.js 3.11.174) │
│ └── pdf.worker.min.js (bundled worker) │
├─────────────────────────────────────────────────┤
│ lib/ │
│ └── translator.js LLM API abstraction layer │
│ - OpenAI-compatible (GPT, DeepSeek, Qwen) │
│ - Anthropic Claude │
│ - Google Gemini │
│ - Ollama (local models) │
│ - Batch mode with numbered segment markers │
├─────────────────────────────────────────────────┤
│ background.js Service worker │
│ - Message routing (translate, cache, PDF) │
│ - Translation cache (Map, LRU eviction) │
│ - PDF/Office file detection & redirect │
│ - Context menu creation │
├─────────────────────────────────────────────────┤
│ i18n.js JS-based localization │
│ - Browser language detection │
│ - data-i18n attribute processor │
│ - en, zh_CN, ja, ko translations │
├─────────────────────────────────────────────────┤
│ _locales/ Chrome i18n (extension name) │
│ manifest.json │
│ README.md │
└─────────────────────────────────────────────────┘
- Clone or download this repository
- Go to
chrome://extensionsin Chrome - Enable Developer mode (top right toggle)
- Click Load unpacked
- Select the project directory
- Click the extension icon → Settings → configure your API Key
- Open the extension popup
- Go to Settings tab
- Select your API provider (OpenAI-compatible, Anthropic, Gemini, or Ollama)
- Enter your API URL and API Key (not needed for Ollama)
- Choose a model
- Set source/target languages
- Click Save Settings
- Click Test Connection to verify
- Click the extension icon → Translate Entire Page
- Or right-click on the page → Translate Entire Page
- Click ↩ Show Original / 🌐 Show Translation to toggle
- Click ✕ to close (translation is cached for the session)
- Select text on any page → click the floating 🌐 Translate button
- Or select text + press
Alt+T(Option+Ton macOS) - Or right-click selected text → Translate Selected Text
- The result appears in a floating tooltip (or sidebar, depending on settings)
- Navigate to any PDF URL or open a local PDF file
- The PDF viewer opens automatically with the PDF rendered
- Click 🌐 Translate to translate
- Translation appears in a panel below each page
- Use ← → arrow keys or buttons to navigate pages
- Open the extension popup → Translate tab
- Type or paste text → click Translate
- Languages are auto-saved when changed in the popup — no need to click "Save Settings"
- Install and start Ollama
- Set environment variable
OLLAMA_ORIGINS=*before starting Ollama (see Ollama CORS Configuration below) - Open the extension popup → Settings → select Ollama (local model)
- Click 🔄 to refresh the model list — installed local models appear automatically
- Select a model, set source/target languages, and start translating
- Go to Settings → Custom Prompt section
- Select an existing template from the dropdown or write a new one
- Use
{src}and{tgt}variables for source/target language placeholders - Click 💾 Save to create a new template, or ✏️ Update to modify the current one
- In batch translation mode, numbered
[#N]markers are automatically appended
Page text is collected, grouped into ~1000-character chunks, and translated by 4 concurrent workers:
Worker 1: [Chunk 0] ──await── [Chunk 4] ──await── ...
Worker 2: [Chunk 1] ──await── [Chunk 5] ──await── ...
Worker 3: [Chunk 2] ──await── [Chunk 6] ──await── ...
Worker 4: [Chunk 3] ──await── [Chunk 7] ──await── ...
Each worker atomically grabs the next chunk index (nextIdx++), safe in JS's single-threaded event loop.
Instead of unreliable separator characters, text segments use numbered markers:
Input:
[#1]
Hello world
[#2]
Good morning
Output:
[#1]
你好世界
[#2]
早上好
The system prompt instructs the LLM to preserve markers. Response parsing uses regex to extract each numbered segment, with null fallback for missing segments (keeps original text).
- Level 1 — Content script memory (
fullPageState): Instant toggle within the same page session - Level 2 — Service worker memory (
translationCacheMap): Persists across page refreshes/navigation. Keyed byURL::targetLang. LRU eviction at 80 entries.
When re-visiting a previously translated page:
- Content script collects text nodes
- Queries background cache → gets
[[original, translated], ...]pairs - Content-matches each node → applies cached translations instantly
- Only new/changed content triggers API calls
PDF pages are rendered using pdf.js (bundled, v3.11.174):
- Each page rendered to canvas at 1.5x scale
- Canvas set as background image on page wrapper div
- Text extracted via
page.getTextContent(), grouped into paragraphs by spatial clustering (Y-proximity → lines → paragraphs by gap size) - Translation panel below each page shows original/translated side-by-side
On macOS, Option+T produces the † character. The extension uses e.code === 'KeyT' (keyboard-layout independent) instead of e.key === 't'.
Chrome extensions run under the chrome-extension:// origin, which is blocked by Ollama's default CORS policy. To allow the extension to connect to your local Ollama instance:
macOS (Ollama.app):
# Set persistent environment variable
launchctl setenv OLLAMA_ORIGINS "*"
# Then quit and restart Ollama (menubar → Quit → reopen)Linux / Terminal launch:
OLLAMA_ORIGINS=* ollama serveWindows (PowerShell):
$env:OLLAMA_ORIGINS="*"; ollama servePersistent (Linux systemd):
# /etc/systemd/system/ollama.service.d/override.conf
[Service]
Environment="OLLAMA_ORIGINS=*"When you change the source or target language in the popup dropdowns, the extension automatically saves the selection to storage. This means:
- Selection translation immediately uses the updated language
- Full page translation uses the updated language
- No need to click "Save Settings" for language changes to take effect
When the extension is reloaded (e.g., after updating at chrome://extensions), content scripts already running on pages lose their connection to the extension runtime. The extension detects this and shows a prompt asking whether to refresh the page to re-establish the connection.
| Permission | Reason |
|---|---|
storage |
Save user settings |
contextMenus |
Right-click translation menu |
activeTab |
Access current tab for translation |
scripting |
Inject content scripts |
webNavigation |
Detect PDF/Office file navigation |
tabs |
Open PDF viewer, read tab URLs |
host_permissions |
Access page content for translation, file:// for local files |
Auto-detect, English, 中文, 日本語, 한국어, Français, Deutsch, Español, Русский
- OpenAI (GPT-4o, GPT-4o-mini, GPT-4-turbo)
- Anthropic Claude (Sonnet 4, Haiku 3.5, Opus 3)
- Google Gemini (2.5 Flash, 2.5 Pro, 2.0 Flash)
- DeepSeek (chat, reasoner)
- Qwen (Plus, Max)
- GLM (4, 4v)
- Ollama (local models) — llama3.2, qwen2.5, gemma2, mistral, phi3, and any model pulled locally
- Any OpenAI-compatible API
MIT