# Offline Translator: Firefox translation models on an Android phone

> Offline Translator is a Kotlin Android app that translates text, documents and images entirely on the device, using the translation models Firefox ships, PaddleOCR for images and a Wiktionary-derived dictionary. It is GPL-3.0, funded by NLnet, and it trades the reach of a cloud service for a fixed list of language packs.

**DavidVentura/offline-translator** — Use Firefox Translation Models for on-device translation on Android

- Repository: https://github.com/DavidVentura/offline-translator
- Stars: 1,311 · Forks: 54
- Language: Kotlin
- License: GPL-3.0
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/davidventura-offline-translator

## Translation with no request leaving the device

Offline Translator is an Android app for text, PDF and ODT document and image translation that works completely offline using on-device models. The mechanism is described plainly: you download language packs once, and translation then happens on your device with no requests sent to external servers. That single property is what the project is built around, and it is the reason to choose it over a hosted translator when the text is a contract, a medical record, a private chat, or anything a reader would not want logged by a third party. The app is a full Android application, package id dev.davidv.translator, listed on F-Droid, mirrored to Codeberg as davidv/offline-translator, and licensed under GPL-3.0. It was funded through the NGI Mobifree Fund established by NLnet, which tells you something about the project's shape: the funding body cares about a mobile internet commons, and the app has no advertising and no account.

## The models are borrowed from other projects, and that shapes the result

The technology section is the honest part of the project description, because it makes clear that this is an integration project rather than a research effort. Translation models come from Firefox's translations models. OCR comes from PaddleOCR. Automatic language detection is done via cld2. The dictionary is based on Wiktionary data exported by Kaikki, and for Japanese there is a second word dictionary, Mecab, used for transliterating Kanji. Text to speech draws on a set of voice engines, including Piper, Coqui, Kokoro, MMS from Hugging Face, Sherpa ONNX and Mimic3. PDF work is done with mupdf and lopdf. The inference engines are slimt for translation models, and MNN for Paddle OCR, TTS and document alignment. The practical consequence is that the quality ceiling of the app is set by those upstream projects, and the app's contribution is making them usable together on a phone: pick the right engine per task, keep the models on device, and make the whole thing reachable from the Android share sheet.

## Text, images and documents arrive through the share sheet

The user interface is built around Android's sharing conventions, which is the right choice for a translator because the text you want translated is usually sitting in some other app. The project's own table of features and triggers describes it. You share text from any app, or type directly. You long-press text in any app and tap Translate in the copy and share bar for a mini popup. You share a link to translate a URL, and you share an image to translate it. Documents are opened through a documents tab inside the app. Dictionary lookup happens by long-pressing a word inside the app, and the TTS voice and speed are changed by long-pressing the play button. Because the image path goes through OCR, translating an image is the heaviest of these operations, and the PaddleOCR character sets are managed by dedicated scripts in the repository. Automatic language detection and transliteration for non-latin scripts are supported, which matters for a language pair such as Japanese to English where the output script is different from the input.

## Building it happens in a container that mirrors the F-Droid build server

The build is reproducible in a way that is worth noting, because the Dockerfile is based on the F-Droid build server image:

```dockerfile
FROM registry.gitlab.com/fdroid/fdroidserver:buildserver
```

That base image brings the Android SDK and the expectations of the F-Droid build environment along with it, and the Dockerfile layers on the NDK at version 28.0.13004108, the android-34 platform and build-tools 34.0.0, cmake, ninja, libclang, and a Rust toolchain pinned to 1.88.0 with the three Android targets added and cargo-ndk 4.1.2 installed. The Rust part is not incidental, since the inference engines are native libraries that have to be cross-compiled for each ABI. The build itself is triggered by one command:

```sh
bash build.sh
```

which runs the build in a Docker container matching the CI environment. Signing is a separate step that takes the keystore, the password and the alias:

```sh
bash sign-apk.sh keystore.jks keystorepass pass alias
```

and the signed copies land in signed/translator-{arm64,armv7}-${version}.apk. The Makefile wraps the same thing with a build target plus lint and check targets, where lint greps for stray println calls, runs a script that checks for extra translations, and runs ktlintCheck, and check runs detekt and detektHardcodedStrings.

## Installing language packs on a device with no internet

The offline promise has an awkward edge: the language packs themselves have to come from somewhere. The project handles this with a manual offline setup path. If you want to use the app on a device with no internet access, you can put the language files in the Documents/dev.davidv.translator directory, and the details are in OFFLINE_SETUP.md in the repository root. The tree shows how much machinery sits behind this. There are catalog_ scripts for the adblock, the base catalog, document detection, the mirror, the PaddleOCR data, the TTS catalogue, its samples and the upstream sources, and separate data_sources/ and catalog_sources/ directories, with download_bucket.py, download_offline.py, generate_index.py, extract_ppocr_charsets.py and regen_index.sh for generating and indexing the packs. The app is also published to a ZapStore channel, with zapstore.yaml and zapstore-publish.py in the tree, and the project publishes a SHA-256 hash of its signing certificate so that a user can check that the build they installed is the one the maintainer signed.

## Where the offline model shows its edges

The cost of offline operation is that the capability set is fixed by what someone packaged. The supported language list is long, running from Albanian through Chinese in both simplified and traditional, Japanese, Korean, Hebrew, Hindi, Persian, Uyghur and Vietnamese to Swahili and Tagalog, but it is a list of packs that exist, not an open set, and the project does not document a way to bring your own model. Two narrower limits follow. The TTS voice list depends on the specific voice engines bundled, so the choice of voice is bounded by what Piper, Coqui, Kokoro, MMS, Sherpa ONNX and Mimic3 provide, and a voice that is not in those sets cannot simply be added. Image translation depends on PaddleOCR recognising the script, so an image in a script PaddleOCR handles poorly will be recognised poorly before translation even starts. And the model download is a real cost: the packs contain the full translation models, so storage and download time are paid up front, on a device where the user may not have a fast connection. The app is also Android only, and the F-Droid listing, the GitHub release and the ZapStore channel are separate distribution paths, so a user can end up on different versions of the same app.

## How this differs from a cloud translation service

The alternative is a network translation API, and the difference in approach is entirely about where the text goes. A cloud service can offer broad language coverage, models that are updated on the server without the user doing anything, and a quality that is often ahead of what a phone can run. The price is that the text leaves the device, the service is reachable only with connectivity, and the terms of a third party govern the handling of the content. Offline Translator inverts that: the models are downloaded once, the text never leaves, and the app works with no network at all after the packs are in place, which is why the manual setup path exists. The cost is exactly the model ceiling described above, plus the fact that improving quality means waiting for the app to repackage newer upstream models. There is a third path, which is a general purpose offline translation stack of the kind that runs on a desktop, and the difference there is the Android integration, since this app plugs into the share sheet and the clipboard rather than making you open a file and copy text.

## Conclusion

Adopt Offline Translator if you translate sensitive documents, work on a device with no reliable connectivity, or want translation that does not phone a server, and if the languages on the list cover what you need. Do not adopt it as a general purpose translator, because the language set is fixed by the available packs and the project does not document custom models. Before you install, verify the signing certificate SHA-256 against the value published in the README, and note that the F-Droid listing and the GitHub release are separate distribution channels with the most recent release at v0.8.5 from 2026-09-09.

## FAQ

### Does Offline Translator send my text to a server?

No. The project states that language packs contain the full translation models and that translation happens on your device, with no requests sent to external servers, and the app is fully offline once the packs are downloaded.

### Which translation models does Offline Translator use?

The translation models are Firefox's translations models. OCR comes from PaddleOCR, language detection from cld2, and the dictionary is based on Wiktionary data exported by Kaikki, with a second Mecab dictionary for Japanese kanji transliteration.

### Can I use Offline Translator on a device with no internet access?

Yes. The project documents a manual offline setup where you place the language files in the Documents/dev.davidv.translator directory, with the details in OFFLINE_SETUP.md in the repository root.

### How do I build the app?

Run `bash build.sh`, which triggers a build in a Docker container matching the CI environment, based on the F-Droid build server image. Signing the resulting APKs is a separate step using `bash sign-apk.sh keystore.jks keystorepass pass alias`.

### What licence is Offline Translator released under?

The project is licensed under GPL-3.0, with the LICENSE file at the root of the repository. The most recent listed release is v0.8.5 from 2026-09-09.

## Sources

- [DavidVentura/offline-translator on GitHub](https://github.com/DavidVentura/offline-translator)
- [Issues](https://github.com/DavidVentura/offline-translator/issues)
- [License: GPL-3.0](https://github.com/DavidVentura/offline-translator/blob/master/LICENSE)
- [README](https://github.com/DavidVentura/offline-translator/blob/master/README.md)
- [Releases](https://github.com/DavidVentura/offline-translator/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/davidventura-offline-translator
