A deterministic, on-device translator for macOS. A UNIX-style command and a drop-in HTTP server for DeepL, LibreTranslate, and Google v2 — all running 100% on-device using Apple's Translation framework.
translate is a tiny macOS CLI built as a UNIX filter: stdin in, stdout out, stderr for errors and optional progress. It uses Apple's on-device Translation framework on macOS Tahoe, auto-detects the source language with NaturalLanguage, reuses a single translation session per run, and batches work wherever possible. With --serve it exposes the same engine over HTTP, byte-compatible with DeepL /v2/*, LibreTranslate /translate /detect /languages, and Google /language/translate/v2/* — so existing client libraries point at it unchanged.
No cloud calls, no API keys, no telemetry, no LLMs, no third-party translation libraries. Apple's Translation framework is a deterministic neural translation engine — same input, same output, every time.
| Mode | Command | Purpose |
|---|---|---|
| UNIX tool | translate --to en |
Pipe-friendly filter, exit codes, JSON/NDJSON output |
| HTTP server | translate --serve |
Drop-in for DeepL, LibreTranslate, Google v2 clients |
- macOS 26 Tahoe or newer
- Apple silicon Mac
- Swift 6 toolchain (Command Line Tools with the macOS 26.4 SDK)
- Translation language models installed — see docs/install-translation-models.md
git clone https://github.com/Arthur-Ficial/translate.git
cd translate
make installmake install copies the release binary to /usr/local/bin/translate and creates the short alias /usr/local/bin/ueb.
echo "hallo welt" | translate --to en
translate --to de "hello world" "good night"
pbpaste | translate --to en | pbcopy
translate --to ja --format ndjson < sentences.txt | jq -r .dst
translate --install de-en
translate --detect-only < unknown.txttranslate --serve --port 8989Drop-in for DeepL clients (Python deepl, Node deepl-node):
import deepl
translator = deepl.Translator("any-token", server_url="http://localhost:8989")
print(translator.translate_text("Hallo Welt", target_lang="EN"))curl -s -X POST http://localhost:8989/v2/translate \
--data-urlencode "text=Hallo Welt" \
--data-urlencode "target_lang=EN"
# {"translations":[{"detected_source_language":"DE","text":"Hello World"}]}Drop-in for LibreTranslate clients (libretranslatepy, raw requests):
import requests
r = requests.post("http://localhost:8989/translate",
json={"q": "Hallo Welt", "source": "auto", "target": "en"})
print(r.json())
# {"detectedLanguage":{"confidence":97,"language":"de"},"translatedText":"Hello World"}Drop-in for the v2 REST API. Override the base URL in your client.
curl -s -X POST "http://localhost:8989/language/translate/v2" \
--data-urlencode "q=Hallo Welt" \
--data-urlencode "target=en"
# {"data":{"translations":[{"detectedSourceLanguage":"de","translatedText":"Hello World"}]}}| Flag | Meaning |
|---|---|
--to <lang> |
Target language, e.g. en, de, ja, de-AT |
--from <lang> |
Source language; skips auto-detection |
--detect-only |
Print detected language and confidence, then exit |
--format <mode> |
plain (default), json, ndjson |
--preserve-newlines |
Preserve newline structure; default |
--no-preserve-newlines |
Allow paragraph-level reflow |
--batch |
Treat each stdin line as an independent unit |
--file <path> |
Translate a UTF-8 file; repeatable |
--install <pair> |
Prepare/download a pair such as de-en |
--installed |
List installed language pairs |
--available |
List supported language pairs |
--no-install |
Fail with exit 4 if a required model is missing |
--langs <a,b,c> |
Detection hints for short ambiguous input |
--quiet |
Suppress progress on stderr |
--serve |
Run as HTTP server |
--port <n> |
Server port (default 8989) |
--host <addr> |
Server bind address (default 127.0.0.1) |
--api-key <k> |
Optional client auth token |
On-device translation keeps source text local, reduces latency, works offline after models are installed, and needs no API key, account, usage quota, proxy, or billing setup. It is also easier to script safely: the same input, model version, and flags produce byte-stable output without a remote service changing behavior between runs.
translate --install de-en # prepare a pair
translate --installed # list installed pairs
translate --available # list supported pairs on this OSFor scripts, prevent automatic model preparation:
translate --from de --to en --no-install < input.txtIf the model is missing, the command exits with code 4.
translate does not translate fenced code blocks, inline backtick spans, URLs, or email addresses. Those spans pass through unchanged.
| Code | Meaning |
|---|---|
| 0 | OK |
| 1 | input or usage error |
| 2 | translation failure |
| 3 | unsupported OS |
| 4 | model not installed and --no-install set |
| 5 | unsupported language pair |
make test # unit + Swift HTTP integration tests
make test-real-clients # exercise the server with real Python clients
# (deepl, libretranslatepy, requests, google-cloud-translate)The unit suite drives every codec and every endpoint via real loopback HTTP. The real-client suite proves drop-in compatibility with three production translation APIs.
See EXAMPLE.md for end-to-end CLI and server scenarios.
make benchReference targets on Apple silicon with installed low-latency models:
| Scenario | Target |
|---|---|
| Cold start, installed model | < 200 ms |
| Batch short sentences | ≥ 500 sentences/sec |
| Typical resident memory | < 300 MB |
| Network calls during translation | 0 |
MIT — see LICENSE.