The Problem
Organizations that need a self‑hosted, offline‑capable translation service often encounter code that is hard to maintain, lacks reproducible builds, and exposes a wildcard CORS policy. These friction points increase operational risk and slow down secure deployments.
What This Does
LibreTranslate is a free, open‑source Machine Translation API powered by the Argos Translate library. The repository contains 214 files organised into two projects: libretranslate/ (172 files) and scripts/ (8 files). Core translation logic lives in libretranslate/app.py (the primary API handler), while supporting modules such as storage.py, api_keys.py, cache.py, secret.py, detect.py, language.py, and locales.py provide backend services, key management, caching, language detection, and i18n handling.
Execution starts at the main entry point in libretranslate/main.py:268, which calls create_app to initialise the framework (Flask/Django/Express‑style) and then runs the server. The translation pipeline flows through translate → iso2model → language‑model lookup, with get_storage (called from 12 places) and get_req_api_key (called from 5 places) as the most‑touched functions. A secondary entry point spec at libretranslate/app.py:1330 generates the OpenAPI specification, reaching 22 functions.
The internal call graph resolves 164 self‑call edges; notable hubs are get_storage (12 callers), get_str (6 callers), and get_json_dict (5 callers). Three modules (libretranslate/__init__.py, libretranslate/main.py, libretranslate/app.py) participate in a circular import dependency, and app.py contains 1171 lines of code, creating high cognitive load and deep nesting (max indentation depth 8).
How It Is Wired
- Entry points:
main(libretranslate/main.py:268) reaches 24 functions;spec(libretranslate/app.py:1330) reaches 22 functions;translate_file(libretranslate/app.py:883) reaches 16 functions. - Outbound effects: 16 functions make network calls (e.g.,
spec → lazy_swag → swag_eval → func → lookupviarequests.post). File system touches occur viamain → create_app [os.mkdir],translate_file → get_upload_dir [os.mkdir], and database reads/writes are performed byapi_keys.py(reads/writes a DB) andcache.py(cryptographic secret generation). - Responsibility map (functions per file, callers, and effects):
libretranslate/app.py– 43 functions, called from 4 files, calls into 10, reads/writes files, definesget_version,get_upload_dir,get_req_api_key,get_req_secret,get_json_dict.libretranslate/storage.py– 43 functions/3 types, called from 5 files, definesget_storage,exists,set_bool,get_bool,set_int.libretranslate/api_keys.py– 7 functions/2 types, called from 2 files, reads/writes files and a DB, makes an outbound network call for key lookup.libretranslate/cache.py– 6 functions/1 class, called from 2 files, performs a cryptographic secret‑generating operation.libretranslate/secret.py– 14 functions, called from 2 files, definesto_base,obfuscate,generate_secret,rotate_secrets,secret_match.libretranslate/detect.py– 6 functions/2 classes, called from 2 files.libretranslate/language.py– 7 functions, called from 2 files, definesiso2model,model2iso,load_languages,load_lang_codes,get_language_with_fallback.libretranslate/main.py– 4 functions, called from 2 files, definesget_parser,get_args,main,redirect.libretranslate/locales.py– 7 functions, called from 1 file.- Module graph:
libretranslate/appimports fromlibretranslate/__init__andlibretranslate/main;libretranslate/mainimports fromlibretranslate/app, creating a cycle that inflates instability (instability 0.7 forapp, 0.29 for__init__andmain).
How To Use It
Setup
- Install the Python package:
pip install -e .(pyproject.toml present). - Or build and run via Docker:
docker compose up -d(docker‑compose.yml and docker/Dockerfile exist).
Configuration
- The API reads environment variables and a
.env‑style config; seelibretranslate/app.pyfor the exact variables it references (e.g.,LIBRETRANSLATE_SECRET,LIBRETRANSLATE_API_KEY). - CORS is currently set to
"*"inlibretranslate/app.py– replace with an explicit allow list for production.
Running it
- Start the server from the CLI:
python -m libretranslate.main(entry pointmain.py:268). - For HTTPS behind a reverse proxy, use the provided Docker Compose files or the
k8s.yamlmanifest.
Real‑World Use
A CI pipeline fetches translated subtitles for a multilingual video platform. The backend calls POST /translate with { "q": "text", "source": "en", "target": "fr" }. The request is authenticated via an API key stored in api_keys.py, routed through storage.py for request‑rate limits, and cached by cache.py to avoid repeated model loads. If the key is missing, api_keys.lookup returns a 401, and the caller retries with a fallback key.
import requests
resp = requests.post(
"http://localhost:5000/translate",
json={"q": "Hello world", "source": "en", "target": "es"},
headers={"X-Api-Key": "my-secret-key"}
)
print(resp.json()["translatedText"])
Code Health & Issues
- High: Pin third‑party GitHub Actions to commit SHAs –
.github/workflowscurrently uses@v2,@v4tags forpierotofy/issuewhiz,docker/setup-qemu-action,docker/setup-buildx-action,docker/login-action; a tag can move and bring new code with secrets. - High: Commit a lockfile beside
pyproject.toml– no lockfile is present, so transitive dependencies may differ between test and production builds. - High: Replace the wildcard CORS origin with an explicit allow list –
libretranslate/app.pysetsAccess-Control-Allow-Origin: *"; pairing with credentials is disallowed by browsers. - Medium: Declare least‑privilege permissions for
GITHUB_TOKEN– 3 workflows have nopermissionsblock; addcontents: readat the top and widen per job. - Medium: Enable Dependabot or Renovate – no update bot configured; add
.github/dependabot.ymlfor Python and GitHub‑Actions ecosystems. - Medium: Pin container base image by digest –
docker/Dockerfileusespython:3.11.14-slim-bookwormwithout a digest; pin viapython:3.11.14-slim-bookworm@sha256<digest>and enable Dependabot Docker monitoring. - Medium: Gate pull requests on a dependency vulnerability scan – CI lacks dependency review; add
dependency-review-actiononpull_requestorosv-scanneron push. - Medium: Set
persist-credentials: falseon checkout –publish-package.ymlkeeps the token; addwith: persist-credentials: falseand pass explicit tokens only to the push step. - Low: Set
timeout-minuteson workflow jobs – 4 jobs have no timeout; add realistic bounds to prevent overlapping runs.
The Bottom Line
LibreTranslate delivers a capable, self‑hosted translation API with solid i18n support and Docker/Kubernetes deployment options. However, the codebase suffers from circular imports, an oversized app.py, missing dependency lockfile, and a permissive CORS policy that together raise operational risk. Teams comfortable with Docker and willing to tighten security posture (pin actions, add lockfile, restrict CORS) can operate it safely; others may need to invest in refactoring the import graph and cleaning up CI configuration before production use.