This repository has no description
JavaScript 100%
CSS <1%

README.md

Local Translate #

Translates posts on-device, using the same WASM engine (bergamot/browsermt) Firefox's own Translations feature uses — no text ever leaves the device for the local engine path. Falls back to a remote LLM endpoint (Ollama, any OpenAI-compatible API, or Anthropic) when either the host doesn't support local WASM translation yet, or the user prefers it.

The remote-endpoint fallback has no address baked in — not even a cloud provider's. It always goes through app.requestFetchPermission/app.fetch: the user types in whatever URL they want (local Ollama, their own OpenAI-compatible proxy, a cloud API, anything) and grants that origin network access before it's ever used. A local-first translation plugin shipping with api.openai.com pre-allowlisted in its manifest would look like it phones home by default, which it doesn't.

Requirements #

Local (on-device) translation needs a handful of core changes to the Impro host itself — see the plugin-wasm-translation-support branch in impro/. Without them this plugin still works, using the remote-endpoint backend only; the settings tab explains this and hides the local option when it detects the host doesn't support it.

The one piece that needs infrastructure of its own: the model registry and per-language model files come from mozilla/translations' real, current production data (a Google Cloud Storage bucket). Verified directly: this bucket does have a CORS policy, but it's an origin allowlist (Origin: https://mozilla.github.io gets Access-Control-Allow-Origin back; arbitrary other origins, including this plugin's, don't) rather than a wildcard — there's no way to get this project's own origin added to that list. infra/cors-relay-worker.js is a small Cloudflare Worker that relays both the registry and the model files, adding CORS headers and edge-caching every response (these files are content-addressed/immutable, so caching forever at the edge is always correct). Combined with the plugin's own app.binaryCache (each end user downloads a given file at most once, ever), actual origin bandwidth through this relay is bounded by the number of distinct files ever requested, not by request volume or user count — and Cloudflare Workers pricing is request/CPU-based, not bandwidth-based, so this is meaningfully cheaper to run than a naive proxy might suggest (worth confirming against your actual plan before relying on that).

Deploy it, then point MODEL_CDN_BASE_URL in src/engine/modelCdn.js (and the matching permissions.fetch entry in manifest.json) at wherever it ends up. The engine .wasm itself is the one piece that doesn't need this — jsdelivr's npm mirror sets CORS headers correctly and is fetched directly.

Development #

npm install --save ../impro/impro-plugin   # point at the local SDK, per the workspace README
npm run watch

Symlink into impro/plugins-local/ and run impro's own dev server per the workspace README's plugin-development workflow.

License note #

src/vendor/bergamot-translator-worker.js and the loading approach in src/engine/bergamotEngine.js are vendored/adapted from bergamot-translator (MPL-2.0). See the comments at the top of each file for exactly what changed and why.