Repository navigation
LuaUI CustomText
Window scripts should not hard-code the text a player reads. They pull it from the
client's language file, CustomText.bmd, so the same window shows the right language
and you can edit or translate every string in one place without touching the Lua.
This page covers the whole round trip: reading a string from a script, adding your own section, and encoding the file the client ships with.
lbl.text = Lang.GetText(Lang.TYPE.ItemBank, 16)-
Lang.TYPEis built at runtime from the sectionsCustomText.bmdactually contains, keyed by their XML node name: a<ItemBank>section becomesLang.TYPE.ItemBank. Add a section to the file and it appears here on its own - there is no list in Lua to keep in step. -
Lang.GetText(textType, id)takes one of thoseLang.TYPEvalues and the entry'sidwithin that section, and returns the string. - A missing entry never returns
nil- it comes back as the placeholder"Missing Text: <id>". Guard against it when a string may not be present yet, and fall back to an in-script default:
local function T(id, fallback)
local s = Lang.GetText(Lang.TYPE.ItemBank, id)
if s == nil or s == "" or s:match("^Missing Text: %d+$") then
return fallback
end
return s
end
lbl.text = T(16, "Zen") -- localised, English when the file has no entry 16ui.LangText(section, id, fallback) is that guard packaged, so you rarely need to
write it out. It names the section by its XML node name rather than through
Lang.TYPE, which means it also copes with the section being absent altogether:
local ui = require("UI")
lbl.text = ui.LangText("ItemBank", 16, "Zen")It returns the fallback when the section is missing, when the id is missing, and
when the placeholder comes back. Reach for Lang.GetText directly only when you
want the placeholder to show, which is useful while filling the file in.
Use either for text a player reads and that you want to keep in the language file. A fixed in-script string is fine for anything else - debug labels, internal names.
The strings live in Data\Local\CustomText.xml, one section per window under the
<Language> root:
<Language Password="xxxxxxxx">
...
<CoinExchange>
<Msg ID="0" Text="Coin Exchange" />
<Msg ID="1" Text="Convert" />
<Msg ID="2" Text="Not enough WCoin." />
</CoinExchange>
</Language>- The node name is the
Lang.TYPEkey -<CoinExchange>becomesLang.TYPE.CoinExchange. Nothing in Lua or the DLL has to be told the section exists; it appears the moment the file carries it. - Each
<Msg ID="n" Text="..." />is one entry.IDis the number you pass as the second argument toLang.GetText;Textis what comes back. - Keep the
IDs stable once your window ships - the script refers to them by number, so renumbering an existing section changes what every label reads.
The client reads CustomText.bmd, not the .xml. The .xml is your editable
source; the .bmd is the packed file the client actually loads.
After editing the .xml, re-encode it with the ServerInfo (ToolKit) XML editor. It
packs the .xml into the .bmd using the Password attribute on the <Language>
root (max 8 characters). Ship the resulting .bmd, and keep the .xml as your source -
there is no tool that turns a .bmd back into .xml.
Each time you change a string:
- edit
Data\Local\CustomText.xml; - re-encode to
CustomText.bmdin the ServerInfo (ToolKit) editor; - ship the
.bmdwith the client.
Keeping the strings in CustomText.bmd instead of in the script means one place to
edit or translate them, and the same window renders in whatever language a client
runs - with no script change and no DLL rebuild.
-
Controls - the
UI.*globals and whereLang.GetTextfits -
File map - client file layout,
.uscvs.lua - Lua UI - dev mode and getting started
The link confirmation that ui.ConfirmUrl and a button's url option put on
screen reads its wording from a section of its own, so it can be translated
without touching any script:
<UrlConfirm>
<Msg ID="0" Text="Leaving the game" />
<Msg ID="1" Text="This link opens in your web browser:" />
<Msg ID="2" Text="Continue" />
<Msg ID="3" Text="Cancel" />
</UrlConfirm>The address itself is not part of the prompt. It is drawn on its own line below it, so it stays readable however long it is, and so the player cannot miss where the button is about to take them.
Every id has an English fallback compiled in, so a client whose file has no such
section still shows a complete box. Reach any section the same way from your own
scripts with ui.LangText(section, id, fallback).
MuLua Scripting Plugin | Home
🖼️ Lua UI
- File map
- Creating windows
- Client and server
- Designing a window
- Protocol
- Scrolling and scrollbars
- Controls
- Custom text
- Text input
- Debug console
- Bitmap Slicer
- Encrypting scripts
Maps
Monsters
Shared
Version 1.14 | 2026-09-09