Localization
F5 Combat HUD ships twelve complete languages, each holding exactly 897 keys:
| Code | Language | Code | Language |
|---|---|---|---|
en | English | pl | Polish |
de | German | es | Spanish |
fr | French | pt | Portuguese |
nl | Dutch | cs | Czech |
tr | Turkish | ar | Arabic |
zh | Chinese | th | Thai |
F5Cfg.Locale = 'en'
One language for the whole server — there is no per-player language picker.
Lookup goes: your language → English → the literal text Translation [key] does not exist. The fallback is per key, not per file, so a half-translated file quietly mixes languages rather than breaking.
Naming a language with no file in locales/ is only a warning; the server keeps running entirely in English:
[f5_combathud][config-validator] WARN: F5Cfg.Locale = "it" has no file in locales/ — every message falls back to English
An empty or non-string F5Cfg.Locale is a hard error and the resource does not start.
How the Files Are Built
Locales['en'] = {
['crosshair_enabled'] = 'Crosshair enabled',
['settings_saved'] = 'Settings saved successfully',
['ui_tab_crosshair'] = 'Crosshair',
-- …
}
Two kinds of key live in the same file:
| Prefix | Count | Where it is used |
|---|---|---|
ui_… | 830 | The menu itself. These are the only keys sent to the browser |
| everything else | 67 | Notifications produced by Lua — saves, profile actions, marketplace errors, rate limits. Translated before they are sent |
A key you invent for menu text must start with ui_, or it will never reach the menu.
Adding Your Own Language
- Copy
locales/en.luatolocales/<code>.lua— saylocales/it.lua. - Change the first line to match:
Locales['it'] = {. - Translate the values. Leave every key exactly as it is.
- Set
F5Cfg.Locale = 'it'inconfig/main.lua. - Restart the resource.
No manifest edit is needed — locales/*.lua is loaded as a glob, so a new file is picked up automatically.
Rules that matter
- Keep the keys. A renamed key is a missing key, and falls back to English.
- Keep the placeholders. Substitution is positional:
%sis replaced in order, so a value must carry the same number of%sas the English one, in the same order. There are 60 placeholders across the file. - One key uses
%d.ui_killfeed_preview_scn_fmtships asScenario %d/%d: %sand must keep exactly two%dfollowed by one%s. - Never translate a value to an empty string. An empty translation is treated as "no translation", and the English text baked into the menu stays visible.
- Keep single quotes and escape them properly. Every value in the shipped files is a single-quoted Lua string; an apostrophe inside one needs
\'. - Never delete a key from
en.lua. English is the floor: a key missing from your language falls back to English, but a key missing from English is absent from the menu payload altogether — and the menu writes it straight into the page, so the label renders as the literal wordundefined. - Do not put a bare
%in a non-ui_value. Those go through Lua'sstring.format. A%that is not part of a valid placeholder makes the substitution fail, and the player sees the raw template —Maximum profiles reached (%s)— with nothing in any console. Writepercent, or double the sign in a way you have tested. - Two families of keys look unused and are not.
ui_kd_icon_*(74 keys, including the category tabs) andui_ep_tag_*(6) are looked up by names the menu builds at runtime, so a text search finds no reference. Deleting one does not break the menu — the label falls back to English text built into the interface — but the translation is lost. - Keep the file encoding. The shipped files are UTF-8 without a BOM, with CRLF line endings. A BOM on the first line breaks the Lua chunk.
There is no validation of translation files at startup — a missing key is silently English and a bad %s count silently prints the untranslated template. Open the menu after translating and walk through every tab.
What Is Not Translatable
A number of visible strings deliberately live in config/*.lua instead of locales/, because they are yours to rename rather than ours to translate. They reach the menu exactly as typed:
| String | Where it lives |
|---|---|
| Crosshair type labels — Cross, Dot, Circle… | config/main.lua → CrosshairTypes[].label |
| Hit particle labels — Money Burst, Clown… | config/features.lua → HitParticles.effects[].label |
| Killstreak tier names — DOUBLE KILL, GODLIKE… | config/defaults.lua → Killstreak.thresholds[].name |
| Marketplace tags — competitive, fun, sniper… | config/features.lua → Marketplace.tags.available |
| Weapon category labels — Pistols, Snipers… | config/weapons.lua → categories[].label |
| Crosshair preset names and their category tabs | config/presets.lua |
| Effects preset names, descriptions and tabs | config/presets.lua |
| Font names in the dropdowns | config/limits.lua → Validation.Fonts |
Translating those means editing the config file — and remember that ids stay untouched: rename a label, never an id. See Configuration.
A few more strings are fixed in the interface files themselves:
| String | Where |
|---|---|
NPC — the kill feed's name for a non-player killer or victim, plus Unknown and Player when a name cannot be read | Set in Lua |
KILL — the word next to the killmarker | html/index.html |
BOB / ROB — labels on the crosshair preview figure. The kill feed preview has its own hardcoded sample names | html/index.html and the menu script |
The share-code prefix chips shown in the menu follow config/main.lua: change crosshairPrefix and the chip changes with it.
See Also
- Configuration — the labels that live in config instead of locales
- NUI & Assets — editing
html/index.htmlsafely - Installation — where to set the locale