Multicharacter Selector Languages
VORPEvery word the selection screen draws comes from ConfigSelector.Locales in config_selector.lua. It is a plain Lua table of tables — nothing in this resource is escrowed, so you can read and edit all of it.
The resource has two separate translation systems, and this page is about the first one:
| Covers | Lives in | Chosen by | |
|---|---|---|---|
| Selector locales | The selection screen and its panels | ConfigSelector.Locales | Each player, in Settings |
| VORP translations | The character creator, clothing stores, outfits | shared/translation.lua | Lang in config.lua, server-wide |
They do not share key names, language names or defaults. Translating one does nothing to the other. The second is covered in Two systems, not one further down.
What ships
ConfigSelector.Locales holds seven tables, and all seven are complete at 89 keys each. None of them is partial.
| Table name | Keys | |
|---|---|---|
English | 89 | The reference table. Everything else falls back to it |
Deutsch | 89 | Complete |
Español | 89 | Complete |
Français | 89 | Complete |
Nederlands | 89 | Complete |
Русский | 89 | Complete |
日本語 | 89 | Complete |
Every table carries the same 89 key names as English, with nothing extra and nothing missing. A player switching language does not lose a label anywhere on the screen.
Italian and Romanian are not selection-screen languages
They exist in this resource, but in shared/translation.lua, which drives the character creator and clothing menus and is a different system with a different key structure. Setting Lang = "Italian" gives you Italian creator menus and an English selection screen, because there is no Italian table in ConfigSelector.Locales. See Two systems, not one.
What the 89 keys cover
Grouped by where they appear on screen:
| Area | Keys | Examples |
|---|---|---|
| Character cards and stats | 13 | character, newCharacter, emptySlot, welcomeTitle, playtime, job, unemployed, money, gold |
| Buttons and prompts | 12 | play, create, delete, settings, credits, accept, decline, navigate, enterKey |
| Delete panel | 4 | deleteTitle, deleteConfirm, deleteWrongName, deleteFailed |
| Pose and location pickers | 13 | pose, poseTitle, poseNone, poseDuoTitle, location, locationCount, pickNone |
| Settings panel | 32 | theme, menuStyle, buttonSkin, filter, framing, bgZoom, camOrbit, camHeight, weather, timeOfDay, resetSettings |
| Partner panel | 15 | partner, lover, friend, unpair, tabIncoming, searchHint, searchPlaceholder, withdraw |
The Settings panel is the largest group by a wide margin, because every row label, every segment button and every dropdown placeholder in it is a key. If you are translating a table by hand, that is where most of the work is.
The language dropdown
The dropdown in Settings → Language is built from the table names themselves. config_selector.lua holds no separate list of display labels, so whatever you call a table is exactly what a player sees.
The list is sorted before it is sent to the interface, so the order is alphabetical by name — and because the sort is by byte, the two non-Latin names come last:
Deutsch English Español Français Nederlands Русский 日本語Rename Deutsch to German and the dropdown reads "German" and sorts between Français and Nederlands. Nothing else needs changing — the name is the identifier.
A player's choice is saved on their own PC, in the client KVP entry vorp_character_selector_settings_v1, alongthe rest of their Settings. It survives a restart and a reconnect, and it is per-player: one player reading the screen in Japanese does not change what anyone else sees. Reset settings in that same panel clears the entry and returns them to the server default.
The interface can only send back a name that is a key in ConfigSelector.Locales; anything else is refused and logged. If a language change appears to do nothing, turn on /selectordebug and watch for rejected unknown language in F8.
Which language a player starts in
A player who has never opened Settings gets the server default, and the default comes from Lang in config.lua.
ConfigSelector.UI.language does not take effect in 1.0.0
The setting exists in the ConfigSelector.UI block, commented nil = follow Lang from config.lua, and it is the one entry in that block the loader never reads. The defaults it merges UI over are built by walking a Lua table in which language is set to nil, and a key set to nil is simply absent — so language is never one of the keys considered. Set Lang in config.lua instead. It works, and it is the value the code actually resolves.
Lang is VORP's own setting and has to name a table in shared/translation.lua, not one in ConfigSelector.Locales. The two naming schemes are different, so the selector keeps an alias table to bridge them:
Lang | Selector table it resolves to |
|---|---|
German | Deutsch |
Spanish | Español |
Francais or French | Français |
Dutch | Nederlands |
Russian | Русский |
Japanese | 日本語 |
English is the only name that exists in both files, so it is the only one needing no alias. The resolution order, in full:
- The player's saved choice, if they have one.
- Otherwise
Lang, if it names a table inConfigSelector.Locales. - Otherwise the alias above.
- Otherwise English.
Step 4 is what Lang = "Portugues_PT" and Lang = "Portuguese_BR" land on. Both are valid VORP translations, and the character creator comes out in Portuguese, but there is no Portuguese locale table and no alias for one, so the selection screen stays English until the player picks something in Settings.
The Language row can read "Deutsch" when the screen is English
Lang is stored as the player's language value even when it is not a table name. The dropdown cannot find it among its options, so it displays the first option instead, which sorts to Deutsch. The screen itself is still resolved correctly through the list above. Picking any entry replaces the unmatched value and the row starts telling the truth.
Missing keys fall back to English
Before the interface is handed a locale, the chosen table is merged over a copy of English. A key the chosen table does not define keeps the English value.
This happens in Lua, per key, every time the screen opens or Settings is saved, so:
- A partial table like
Italianis safe to ship. Its 15 gaps read as English rather than as blanks or key names. - A table you are halfway through translating is usable immediately. Translate the Settings panel first and the rest will catch up.
- Deleting a key from a table is a valid way to say "use the English wording here".
- Adding a key to one table only does nothing. The merge starts from English, and the interface only ever asks for keys English defines. A key that exists in
Italianbut not inEnglishis unreachable — which is exactly what happened to those five leftovers.
Adding a language
Open
config_selector.luaand findConfigSelector.Locales— it starts just after the partner settings. Copy the whole["English"]block, from the opening brace to its closing},.Paste it inside
ConfigSelector.Localesand rename the key to the name you want in the dropdown. Write it the way speakers of that language write it —["Português"], not["Portuguese"]— and wrap it in brackets and quotes if it is not a plain identifier. The file is UTF-8, so accented and non-Latin names are fine.Translate the values, leaving every key name untouched. A key name is what the interface looks up; only the string on the right of the
=is read by a human.Keep
%sindeleteConfirm. It is replaced by the character's first name, in bold, at the point where you leave it. The English string is"This cannot be undone. Type %s to confirm."— move%sto wherever your sentence needs the name.Restart the resource:
cfgrestart vorp_characterOpen Settings and the new name is in the dropdown. If you also edited anything under
html/, raise the?v=number on its tag inhtml/index.html— CEF caches by URL.
Do not drop %s from deleteConfirm
The interface splits the string on %s and inserts the name between the halves. With no %s there is only one half, so the name is appended to the end of your sentence instead of landing inside it — which reads as a mistake in a panel asking someone to confirm a permanent delete. With two %s, the first becomes the name and the second disappears.
Nothing else needs touching. ConfigSelector.Locales is read directly, there is no manifest entry per language, and config_selector.lua is already a shared script. Adding a table is one edit to one file.
Two systems, not one
The character creator, the clothing stores and the outfits menu are stock VORP. They read shared/translation.lua through T = Translation.Langs[Lang], and that file ships eight languages:
English Italian Portugues_PT Portuguese_BR Francais German Spanish RomanianThose names are VORP's, they are not the selector's, and the key structure inside them is completely different — nested tables like T.MainMenu, T.MenuClothes and T.PromptLabels rather than the flat list of 89. A translation you write for the selector cannot be reused here, and vice versa.
Lang must name a table in shared/translation.lua
Set Lang = "Deutsch" and Translation.Langs["Deutsch"] is nil, so the first creator menu that reads T throws an error and the character creator stops working. Use German — the alias table above maps it to the selector's Deutsch for you.
Nederlands, Русский and 日本語 have no counterpart in shared/translation.lua at all. They are selector-only: a player can pick them in Settings, and the creator menus stay in whatever Lang is set to.
So on a German server the correct pair is Lang = "German" in config.lua — creator menus in German, selection screen in Deutsch through the alias. On a Dutch server, set Lang to the closest language shared/translation.lua does carry, and expect the creator and the selection screen to differ until you add a Dutch table to shared/translation.lua yourself.
What stays in English
| Why | |
|---|---|
| The Scene Creator panel | creator.js has no translation layer and reads no locale key. The editor is an admin tool |
| Pose and emote labels | Names in animations.lua and config_emotes.lua, not locale keys. Edit the label fields to translate them |
| Location names | Set per location in config.lua and config_scenes.lua |
| Weather names | Passed through from weathersync, shown as the type name. Only scene is translated, through sceneDefault |
| Command names and console output | /scenecreator, /selectordebug and every F8 line |
| Credits rows | Plain strings in ConfigSelector.UI.credits |
The pose browser's own labels are listed on The Multicharacter Emote Library, and the shapes you edit to rename one are under Adding your own.
Where to go next
- Configuration — the rest of
config_selector.lua, including theConfigSelector.UIblock the language default lives in. - Player Guide — the Settings panel as a player sees it.
- Troubleshooting —
/selectordebugand reading its output. - VORP framework — how
Langandvorp_corefit together.
Stuck on a translation, or want a language added to a future build? Ask on Discord.