Moud

Live config and translations#

Some things are better changed without publishing: the price of an item, the length of a round, a message of the day, a text that reads wrong in one language. A game keeps those on the website, and its servers and players read them while they run.

Config#

The Config page of your game holds settings, each with a key, a type and a value. The types are text, number, true or false, and JSON for a table. Scripts on both sides read them:

luau
local roundSeconds = config.get("roundSeconds", 120)
local shop = config.get("shop", {}) -- a JSON setting comes back as a table

The second argument is what config.get gives back while the key has no value, so a script works in a playtest and before you save anything. Saving on the website reaches running servers within a few seconds, and their players right after. config.changed fires once for each key whose value moved:

luau
config.changed:connect(function(key)
    if key == "roundSeconds" then
        roundSeconds = config.get("roundSeconds", 120)
    end
end)

config.keys() lists the keys that have a value.

Tick Secret on a setting that players must not see, such as a webhook address. Secret settings are only sent to your servers; config.get on a client gives back the default for them.

The Beta switch on the page edits a second set read by the servers of the beta channel. Until you save beta settings, beta servers read the live ones. See Live and beta.

Every save is a new version in the history below the form. Roll back to this saves an old version again as the newest one, so the history keeps both.

Limits#

What Limit
Settings per channel 200
Key 64 letters, digits and _ . -
Text value 4096 letters
JSON value 16 KB
All settings together 64 KB
History the last 100 saves per channel

Translations#

The Translations page holds a table of keys and one column per language. Languages use Minecraft's codes, like en_us, fr_fr or de_de. One of them is the default.

luau
label.text = i18n.t("menu.play")
label.text = i18n.t("hud.welcome", { name = player.name }) -- "Welcome, {name}!"

i18n.t looks the key up in the player's Minecraft language, then in another dialect of the same language (fr_ca for a player on fr_fr), then in the default language. A key with no text anywhere comes back as the key itself, which makes a missing text easy to spot. {name} in a text is replaced by the variable of that name.

On a client, i18n.t uses the language of the player running it. A server serves everyone, so pass the player as the third argument there:

luau
local text = i18n.t("round.won", { seconds = 12 }, player)

i18n.locale(player) gives the player's language code, i18n.has(key, locale) tells whether a language has a text for a key, and i18n.locales() lists the languages. i18n.changed fires after the translations change, so an interface can draw its texts again.

You can edit the texts on the page, or upload a file. A csv starts with a key column and has one column per language:

key,en_us,fr_fr
menu.play,Play,Jouer
hud.welcome,"Welcome, {name}!","Bienvenue, {name} !"

A JSON file is either a table of languages, { "en_us": { "menu.play": "Play" } }, or a table of keys, { "menu.play": { "en_us": "Play" } }. Texts in an upload replace the ones with the same key, and the rest stay.

A place can also ship texts with the game in a translations.json at its root, in either JSON shape or as { "defaultLocale": "en_us", "tables": { ... } }. Those work in a playtest, and texts from the website go over them key by key once the game runs on a fleet.

The publish dialog in the editor reads the keys your scripts pass to i18n.t and warns about every language that has no text for one.

Limits#

What Limit
Languages 30
Keys 2000
Key 128 letters, digits and _ . : -
Text 2000 letters
All texts together 512 KB
Upload 400 KB
History the last 30 saves