English | 简体中文
Local, offline translation: Rust + llama.cpp running Qwen3.5 (Q4_K_M). GPLv3.
Model variants: 2B by default, --fast switches to 0.8B, --quality to 4B. Each auto-downloads on first use (ModelScope by default, HuggingFace optional).
A very Unix project: it does one thing — translate text from stdin (or arguments) to stdout — and is designed to be composed with other tools in pipelines. Text in, translation out, nothing else on stdout.
echo "我喜欢编程" | translate # -> I like programming.
cat README.md | translate | less # pipe away
translate --to ja "你好,世界" # -> こんにちは、世界です。
translate --image screenshot.png # image translation (multimodal, not OCR)- Auto-downloads the model on first run (ModelScope by default, HuggingFace optional)
- 9 languages: zh / en / ja / ko / fr / de / ru / es / pt;
--from(defaultauto) and--to, overridable viaTRANSLATE_FROM/TRANSLATE_TO - Image translation via
--imagewith optional region cropping (-x/-y/-w/-h, long forms--width/--height) - Streaming output by default (
--no-streamto disable) - GPU when available (Vulkan / CUDA / Metal), silent CPU fallback otherwise
- stdout carries results only; logs and errors go to stderr
- Optional Firefox extension: right-click "Translate selection" backed by the same binary
Grab an asset from Releases:
| Asset | Notes |
|---|---|
qwen-translate-*-linux-x86_64.tar.xz |
Linux binary; model auto-downloads on first run |
qwen-translate-*-linux-x86_64-with-model.tar.xz |
Same, with the model bundled (works offline) |
qwen-translate_*_amd64.deb / *-1.x86_64.rpm / *-1-x86_64.pkg.tar.zst |
Debian / Fedora / Arch packages (no model) |
qwen-translate-full_* / qwen-translate-full-* |
Same three formats with the model (installed to /usr/share/qwen-translate/, offline-ready) |
qwen-translate-*-macos-arm64.tar.xz (and -with-model) |
macOS (Apple Silicon, Metal-accelerated) |
For tar.xz, extract and run scripts/install.sh (installs to ~/.local/bin). The macOS binary is unsigned; run xattr -d com.apple.quarantine ~/.local/bin/translate once.
# Linux with Vulkan (recommended; falls back to CPU at runtime if no GPU)
cargo build --release --features vulkan
# CPU only
cargo build --release
# Linux with NVIDIA CUDA (needs the CUDA toolkit)
cargo build --release --features cuda
# macOS (Metal)
cargo build --release --features metalBuild deps: cmake, clang, a C++ compiler. Linux Vulkan builds also need SPIRV-Headers:
# Debian/Ubuntu: sudo apt install spirv-headers libvulkan-dev glslang-tools glslc
# Arch: sudo pacman -S spirv-headers
# or a sudo-less local install:
git clone --depth 1 https://github.com/KhronosGroup/SPIRV-Headers /tmp/spirv-headers
cmake -S /tmp/spirv-headers -B /tmp/spirv-headers/build -DCMAKE_INSTALL_PREFIX=$HOME/.local
cmake --build /tmp/spirv-headers/build --target install
CMAKE_PREFIX_PATH=$HOME/.local CPATH=$HOME/.local/include cargo build --release --features vulkantranslate "I love programming" # arguments -> Chinese
echo "我喜欢编程" | translate # stdin pipe -> English
translate < file.txt # file redirect
cat file.txt | translate | less # pipelines compose
translate --to ja "你好" # pick a target language
translate --from ru --to zh "Привет" # pick a source language (default: auto)
TRANSLATE_TO=ja translate "你好" # env var default; CLI flags win
translate --image shot.png # translate a whole image
translate --image shot.png -x 10 -y 20 -w 300 -h 200 # translate a region only
translate --model /path/to.gguf "…" # GGUF path or variant keyword (fast/default/quality)
translate --fast "…" # same as --model fast; --quality/--default likewise
translate --download-only # download all model variants, then exit
translate --download-only --fast # download only the 0.8B variant
translate --download-only --huggingface # download via HuggingFace (default: ModelScope)
translate --no-stream "…" # disable streaming output
translate --gpu-layers 0 "…" # force CPU
translate --model-source huggingface "…" # download via HuggingFace (default: modelscope)
translate --log-level info "…" # llama.cpp internals (default: warn, quiet)Arguments win over stdin; with neither, you get the help text. Decoding is greedy (deterministic).
The model runs in non-thinking mode (Qwen3.5-0.8B is non-thinking by default; /no_think is not supported).
Model lookup order (first hit wins):
--model/QWEN_TRANSLATE_MODEL— a variant keyword (fast/default/quality) or an explicit GGUF path (explicit paths are never auto-downloaded; a missing file is an error)- User data dir:
~/.local/share/qwen-translate/(Linux),~/Library/Application Support/qwen-translate/(macOS) - System dirs:
/usr/local/share/qwen-translate/,/usr/share/qwen-translate/(plus/opt/homebrew/share/qwen-translate/on macOS) — where model-bundled packages install - Otherwise auto-downloads to the user data dir (ModelScope by default)
Pre-download or switch variants with scripts/download-model.sh (VARIANT=default/fast/quality, MODEL_SOURCE, MODEL_REPO env overrides).
The binary is a plain filter, so it composes with anything. A few setups worth stealing:
Save as /usr/local/bin/selection-translation (or anywhere) and chmod +x:
#!/bin/bash
text=$(wl-paste --primary --no-newline 2>/dev/null) # Wayland
[ -z "$text" ] && text=$(xsel --primary --output 2>/dev/null) # X11 fallback
result=$(echo "$text" | translate 2>/tmp/translate.err)
if [ $? -ne 0 ]; then
notify-send "Translation failed" "$(cat /tmp/translate.err)"
else
notify-send "Translation" "$result" --expire-time=15000
fiBind it in KDE: System Settings → Shortcuts → Custom Shortcuts → New → Global Shortcut → Command/URL, trigger e.g. Meta+T, command /usr/local/bin/selection-translation. Select text anywhere, hit the shortcut, get the translation as a notification (on failure you get the stderr message instead). GNOME: Settings → Keyboard → Custom Shortcuts, same idea. On macOS swap wl-paste/xsel for pbpaste. Tip: add --fast if you want the popup quicker.
Take a screenshot into the clipboard (Spectacle, grim -g "$(slurp)" - | wl-copy, …), then:
wl-paste > /tmp/shot.png && translate --image /tmp/shot.png:'<,'>!translate
translate < notes.md | sponge notes.md # in place (sponge from moreutils)
translate < README.md > README.zh.md # into a new file./some-app --help | translate
make 2>&1 | translate # stderr includedwl-paste --watch sh -c 't=$(translate --fast 2>/dev/null) && [ -n "$t" ] && notify-send Translation "$t" --expire-time=8000'| Code | Meaning |
|---|---|
| 0 | success |
| 1 | runtime error (inference, I/O, …) |
| 2 | usage error (bad args, no input, text too long, …) |
| 3 | model error (missing explicit path, load failure, …) |
| 4 | download failure |
| 5 | the model does not support the requested feature (e.g. images) |
-
Install the native messaging host manifest:
scripts/install-native-host.sh
-
Firefox →
about:debugging#/runtime/this-firefox→ "Load Temporary Add-on" → pickfirefox/manifest.json. -
Select text on any page → right-click "翻译选中内容" → a floating panel shows the translation (copyable).
Notes:
- The extension talks to the binary over a long-lived native-messaging connection; the model stays loaded, so only the first translation pays the load cost.
- Temporarily loaded extensions vanish on browser restart; reload via about:debugging, or use
web-ext run. - Restricted pages (
about:*,addons.mozilla.org, …) block content scripts — a browser limitation. - Firefox's built-in translation exposes no API for third-party engines, hence the context-menu + panel approach.
- CI (push / PR): builds on Ubuntu (Vulkan) and macOS (Metal) + real-inference smoke tests (text and image)
- CD (push a
v*tag): builds release assets — per-platform tar.xz (with/without model) + deb / rpm / pacman packages (with/without model) — and creates a draft GitHub Release for manual review before publishing
rm ~/.mozilla/native-messaging-hosts/qwen_translate.json # Linux
rm -rf ~/.local/share/qwen-translate # model & wrapper
rm ~/.local/bin/translate| Problem | Fix |
|---|---|
| Auto-download fails | Run scripts/download-model.sh manually, or --model-source huggingface |
| Panel says the translator can't start | Re-run scripts/install-native-host.sh; check the binary path |
Vulkan build: SPIRV-Headers missing |
See "From source" above |
| GPU not used | Runtime CPU fallback is normal; check vulkaninfo --summary; CUDA builds need the toolkit |