FTWGL Quake3e Translation System

Real-time in-game chat translation, on-screen overlay, and outgoing translated chat — all engine-side. Hosted backend at translate.pugbot.net.

Downloads

Linux x86_64

Native client binary · libcurl statically linked · built May 11, 2026
Download ftw.x64
sha256: 5902c0ee14d9c6c05f97da15febbb3ddbad3284019785589de5077b9eed66a46

Windows x86_64

Standalone .exe · libcurl + Schannel statically linked · built May 11, 2026
Download ftw.exe
sha256: ff0fbceea372215649dbfcd9b19debaa609977ffaecdd5cd42e36f7d2382a570

Checksums: SHA256SUMS

Install: drop the binary into your Urban Terror directory next to Quake3-UrT.x86_64 / Quake3-UrT.exe, mark it executable on Linux (chmod +x ftw-linux-x86_64), then run it. No extra DLLs or shared libraries required — everything (libcurl, TLS via Schannel on Windows / OpenSSL on Linux) is statically linked.
Source & patches: source is not yet published. Patches against upstream ftwgl/ftwgl-quake3e (branch next-interp) and stock ec-/Quake3e are being prepared once we finish in-game testing. This page will be updated with links and a merge request reference when ready.

1. What this is

A chat-translation layer built directly into the FTWGL Quake3e client. It is not a text-file localization table. The client sends chat sentences to a LibreTranslate-compatible HTTP endpoint asynchronously, caches completed translations, and prints them back as [TR] … lines in the console and (optionally) as a chat-style on-screen overlay. The same backend can also be used to translate your own outgoing chat before it reaches the server.

2. What it is for

3. Translation backend

The client expects a LibreTranslate-compatible /translate endpoint that accepts application/x-www-form-urlencoded POST fields:

FieldDescription
qSentence to translate.
sourceSource language, normally auto.
targetTarget language code, e.g. en, es, de, fr.
formatAlways text.
api_keyOptional, only sent if tr_api_key is set.

The default endpoint baked into the binary is this host:

https://translate.pugbot.net/translate

You can self-host with Docker:

docker volume create libretranslate-data
docker run -d \
    --name libretranslate \
    --restart unless-stopped \
    -p 127.0.0.1:5000:5000 \
    -v libretranslate-data:/home/libretranslate/.local \
    libretranslate/libretranslate:latest \
    --host 0.0.0.0 \
    --port 5000 \
    --load-only en,es,de,fr,it,pt,ru

Quick connectivity test:

curl -s https://translate.pugbot.net/translate \
    -d 'q=hola amigo como estas' \
    -d 'source=auto' \
    -d 'target=en' \
    -d 'format=text'

Expected response shape:

{"translatedText":"hello friend how are you"}

4. How to use

Incoming translation

Once the client is connected and tr_enable 1 (the default), every chat / radio line that contains a speaker prefix is translated into tr_language. Translated output appears in the console as:

[TR] PlayerName: hello friend how are you

Repeated identical lines are served instantly from the in-memory cache.

On-screen overlay

With tr_overlay 1 (the default), translated lines also appear stacked on the left side of the screen, fading out over tr_overlay_time seconds.

Clean translation mode

Setting cl_clean_tr 1:

The yellow chat overlay drawn by Urban Terror's cgame (e.g. >Player: hello) is server-driven and outside the engine. To hide it, add cg_chatHeight 0 to your config. Combined with cl_clean_tr 1, only the translation overlay will be visible.

Outgoing (sent) translation

Two ways to send a translated message:

1. Generic, language from tr_say_lang:

/tr_say hola, ¿cómo estás?
/tr_say_team buena suerte
/tr_tell 3 gracias por la ayuda

2. Per-language shortcut commands (force the target regardless of the cvar):

/say_es hello world         → sends Spanish to public chat
/say_de good luck            → sends German
/say_team_pt vamos no spawn  → sends Portuguese to team chat

While the translation request is in flight nothing is sent yet. When the response arrives the client runs the corresponding say / say_team / tell command with the translated text. If the backend fails or times out, the original message is sent so you are never silenced.

5. Full cvar reference

Core translation

CvarDefaultDescription
tr_enable1Master switch for incoming and outgoing translation.
tr_languageen (auto-detected from $LANG)Target language for incoming translation.
tr_sourceautoSource language hint. auto lets the backend detect.
tr_urlhttps://translate.pugbot.net/translateLibreTranslate-compatible endpoint URL.
tr_api_key(empty)Optional API key. Stored as CVAR_PROTECTED.
tr_timeout2500HTTP request timeout in milliseconds (250–10000).
tr_chat_only1If set, only chat-style lines are eligible for translation, not arbitrary console output.
tr_debug0Print backend / queue diagnostics to the console.
tr_hide_original0Suppress the original chat line and only show the translation. On failure the original is shown as a fallback.

Outgoing translation

CvarDefaultDescription
tr_say_langenTarget language for the generic tr_say, tr_say_team, tr_tell commands.

Overlay / clean mode

CvarDefaultDescription
tr_overlay1Show translated lines as an on-screen overlay (0 = console only).
tr_overlay_time8Seconds a translated line stays visible (1–60). Last 25% fades.
tr_overlay_y0Pixel Y position for the top of the overlay. 0 = auto.
cl_clean_tr0Hide engine notify feed and use chat-sized text for the overlay.

6. Full command reference

Translation control

CommandDescription
tr_language <code>Set tr_language and clear the cache.
tr_statusPrint backend URL, current target language, and cache stats (ready / pending / failed).
tr_clearClear the translation cache and any pending HTTP requests.
tr_reloadCompatibility alias for tr_clear. No text files are loaded.

Outgoing chat

CommandDescription
tr_say <message>Translate to tr_say_lang and forward via say.
tr_say_team <message>Translate to tr_say_lang and forward via say_team.
tr_tell <client> <message>Translate to tr_say_lang and forward via tell <client>.

Per-language shortcuts

These ignore tr_say_lang and force the listed target language.

PublicTeamTarget language
say_ensay_team_enEnglish
say_essay_team_esSpanish
say_desay_team_deGerman
say_frsay_team_frFrench
say_itsay_team_itItalian
say_ptsay_team_ptPortuguese
say_rusay_team_ruRussian

7. Code changes (overview)

The full source patches will be published here once in-game testing is complete and the upstream MR has been opened. High-level layout:

FileRole
code/client/tr_system.hPublic API for the translation system.
code/client/tr_system.cImplementation: cache, HTTP, overlay, outgoing queue, commands.
code/client/cl_console.cHook: route prints through translator; flush + draw overlay.
code/client/cl_main.cHook: TR_Init, TR_Shutdown, TR_Frame, TR_UpdateCvars.
MakefileAdds tr_system.c to the client build with USE_TRANSLATION_CURL.

Internals worth knowing

8. Troubleshooting

SymptomFix
No [TR] lines appear.Verify tr_enable 1, tr_status shows the URL, and the backend is reachable. Set tr_debug 1.
Linux: "missing libcurl symbol" when launching an older build.The current binaries on this page are statically linked — you should not see this. If you do, you are running an older build that did dlopen("libcurl.so.4"); install libcurl4 or download a fresh binary above.
Outgoing tr_say sends original text.Backend is unreachable or timing out. Increase tr_timeout, check tr_status, and confirm the URL with curl.
Same-language say_en still hits the network.Set tr_source en explicitly; with tr_source auto the client cannot prove the source matches the target.
cgame yellow chat still shows in clean mode.Add cg_chatHeight 0 to your Urban Terror config. The cgame draws that overlay; the engine cannot suppress it.
Console feed still translates non-chat lines.Set tr_chat_only 1 (default).

9. Known limitations