Translation Gateway¶
Description¶
One translation endpoint for every application in the deployment. It speaks the LibreTranslate API, chooses which engine answers, remembers which engine won earlier comparisons, caches what it has already paid for, and returns a reviewed human translation ahead of any machine output.
Overview¶
The gateway is published at api.translate.{{ DOMAIN_PRIMARY }}. An application points at that one URL; anything that already speaks to LibreTranslate works unchanged.
Four backends are declared as consumer entries in meta/services.yml, each switched on by deploying the role that provides it:
Backend |
Provider |
Reached over |
|---|---|---|
|
the LibreTranslate API |
|
|
FLORES-200 codes, mapped from ISO 639-1 by |
|
|
the LibreTranslate API |
|
|
Ollama’s generate API, as a prompt |
nllb serves 136 of the 184 languages of meta/languages.yml and refuses the other 48, which NLLB-200 has no language code for; flores.py names them. It also refuses a request without a source language, because NLLB carries no detector.
Architecture¶
ARCHITECTURE.md draws Translation Gateway from its own metadata: the containers it runs under compose, what changes under swarm, how each volume is backed, and every role it depends on or is depended upon by.
Section |
Drawn from |
|---|---|
|
|
|
|
|
|
|
Reading the memory¶
The gateway authenticates to Weblate with the API token web-app-weblate writes to the token store after its own deploy. A web service is deployed before the web applications, so on the very first run of a fresh deployment that token does not exist yet: the gateway starts without a memory and answers from the engines. The next deploy renders the stored token and the reviewed strings take precedence from then on.
Features¶
One API:
POST /translate,POST /detectandGET /languagesin LibreTranslate’s request and response shapes.Reviewed strings win: a string approved in
web-app-weblateis returned verbatim and no engine is called.Engine choice that learns: with
svc-ai-s1deployed the router asks the decider which engine should answer, states each engine’s record in the criteria, and after a configured number of comparisons routes straight to the leader.Blind verdict: while sampling, several engines answer the same request and the decider picks between the translations alone, labelled
option-0,option-1, …, never by engine name.Glossary protection: a term the glossary protects must survive the translation; an answer that drops it is rejected rather than cached.
Loud failure: with no engine able to answer, the gateway returns an error. It never hands the untranslated source text back as a translation.
Quick Setup¶
Development¶
Clone, set up the workstation, and deploy Translation Gateway onto the local stack:
git clone https://github.com/infinito-nexus/core.git
cd core
make onboard
make compose-deploy mode=reinstall apps=web-svc-translate full_cycle=false
Production¶
Run the published image to provision the inventory and deploy Translation Gateway to a managed server over SSH. The image logs in as root, so the server must accept root over a public key, and the two key variables are the two halves of one key pair: the private half authenticates the deploy, and the public half is what the deploy keeps authorized for root and installs for the administrator account. Naming a public half that does not belong to the private one locks the running deploy out at its next reconnect.
APP=web-svc-translate
HOST="<your-server>"
DOMAIN="<your-domain>"
TLS_MODE=self_signed
SSH_PUBLIC_KEY="<your-ssh-public-key>"
SSH_PRIVATE_KEY="<your-ssh-private-key-file>"
WORKDIR="$HOME/.infinito.nexus"
mkdir -p "$WORKDIR/inventories"
docker run --rm \
-v "$WORKDIR/inventories:/etc/infinito.nexus/inventories" \
-v "$SSH_PRIVATE_KEY:/run/secrets/deploy_key:ro" \
-e APP="$APP" -e HOST="$HOST" -e DOMAIN="$DOMAIN" -e TLS_MODE="$TLS_MODE" -e SSH_PUBLIC_KEY="$SSH_PUBLIC_KEY" \
ghcr.io/infinito-nexus/core/debian:latest bash -c '
INVENTORY=/etc/infinito.nexus/inventories/production
infinito administration inventory provision "$INVENTORY" \
--inventory-file "$INVENTORY/devices.yml" \
--host "$HOST" \
--include "$APP" \
--vars "{\"TLS_MODE\": \"$TLS_MODE\", \"DOMAIN_PRIMARY\": \"$DOMAIN\", \"SYS_SVC_SSHD_PERMIT_ROOT_LOGIN\": \"prohibit-password\", \"users\": {\"root\": {\"authorized_keys\": [\"$SSH_PUBLIC_KEY\"]}, \"administrator\": {\"authorized_keys\": [\"$SSH_PUBLIC_KEY\"]}}}" &&
infinito administration deploy dedicated "$INVENTORY/devices.yml" \
--password-file "$INVENTORY/.password" \
-u root --private-key /run/secrets/deploy_key \
--diff -vv'
The inventory under $WORKDIR is written by the container as root, including the vault password at inventories/production/.password. Run sudo chown -R "$USER" "$WORKDIR" afterwards to read it.
Credits¶
Part of the Infinito.Nexus Project and maintained by Kevin Veen-Birkenbach. Licensed under the Infinito.Nexus Community License (Non-Commercial).
This role deploys no third-party application, so the licence above is the only one that applies.
Schema¶
flowchart LR
app["application"] -->|POST /translate| gw["web-svc-translate"]
gw -->|reviewed string| wl["web-app-weblate"]
gw -->|which engine| s1["svc-ai-s1"]
gw -->|cache, history, failures| db["PostgreSQL"]
gw --> lt["svc-ai-libretranslate-engine"]
gw --> nl["svc-ai-nllb-engine"]
gw --> lte["svc-ai-ltengine"]
gw --> ol["svc-ai-ollama"]
Boundary¶
This role translates the content users put into the applications, at request time. Two neighbouring mechanisms translate something else, and neither goes through this gateway:
make i18n-translatetranslates this repository’s own.pocatalogues at build time by calling an engine directly. One batch of known strings, with no request to cache and no caller to serve.The core surfaces (the docs site, the dashboard, the logout panel) read those catalogues at render time.
The boundary is build time against request time.