Weblate¶
Description¶
Weblate is a web-based continuous localization platform. It keeps a translation memory, glossaries and a review workflow, so a human translation can be written once, approved, and reused everywhere.
Overview¶
In this deployment Weblate is the memory behind the translation gateway. web-svc-translate asks Weblate first: a string with an approved translation is returned verbatim and no engine is called. Translators reach Weblate’s own UI at web.translate.{{ DOMAIN_PRIMARY }}, through Keycloak when SSO is deployed.
Architecture¶
ARCHITECTURE.md draws Weblate 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 |
|---|---|
|
|
|
|
|
|
|
Features¶
Translation memory: every approved string is reusable by the gateway and by other components.
Glossaries: terms that must survive a translation; the gateway rejects a machine answer that drops one.
Review workflow: a translation becomes authoritative when a reviewer approves it.
API access: the deploy reads the API token Weblate issued for its administrator and persists it in the token store, where the gateway reads it.
Quick Setup¶
Development¶
Clone, set up the workstation, and deploy Weblate onto the local stack:
git clone https://github.com/infinito-nexus/core.git
cd core
make onboard
make compose-deploy mode=reinstall apps=web-app-weblate full_cycle=false
Production¶
Run the published image to provision the inventory and deploy Weblate 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-app-weblate
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).
That licence covers this role. The software it deploys is a separate grant: GPL-3.0-or-later, under which commercial use is allowed. You owe its authors this much: publish the source of changes you distribute. Permission for one of the two is not permission for the other.
Administration¶
The container’s entrypoint provisions the administrator as the Weblate user admin; WEBLATE_ADMIN_NAME is its display name only. The role reads that account’s API token out of Weblate’s own database after the deploy and writes it to sys-token-store under administrator / web-app-weblate.
Networks¶
Weblate is served on clearnet over https and on its onion sibling over plain http, from one container. Upstream builds every absolute URL, including the OIDC redirect_uri, from SITE_URL. files/python/settings_override.py is mounted at /app/data/settings-override.py, the file upstream’s settings_docker.py execs last. It replaces weblate.middleware.ProxyMiddleware and weblate.accounts.strategy.WeblateStrategy with the subclasses in files/python/weblate_network/middleware.py and files/python/weblate_network/strategy.py, mounted as the namespace package weblate_network under /app/infinito, which take scheme and host from the request. They live in two modules because social_django.utils imports the strategy at app-ready time, before it defines the load_strategy that weblate.middleware imports back. The strategy module therefore imports nothing beyond weblate.accounts.strategy. The host is validated against WEBLATE_ALLOWED_HOSTS, which lists every network sibling, the internal service name and the loopback names. The override also turns off SECURE_SSL_REDIRECT and SOCIAL_AUTH_REDIRECT_IS_HTTPS, because the proxy already redirects clearnet http to https. It sets the language cookie to SameSite=Lax.
The subclasses override upstream internals checked against Weblate 2026.9 (weblate/middleware.py ProxyMiddleware.process_request, weblate/accounts/strategy.py WeblateStrategy.build_absolute_uri/request_is_secure/request_host/request_port). Re-check both before raising services.weblate.version. The override refuses to start Weblate when either upstream class path is gone.
WebAuthn second factors stay bound to the clearnet domain (OTP_WEBAUTHN_* follows SITE_URL), and a plain-http onion page is no secure context, so a WebAuthn key cannot be used on the onion sibling. E-mails, feeds and other links Weblate renders from SITE_URL keep naming the clearnet domain.