Translate your menu

Write every label as Text with an English fallback, ship lang files for each Minecraft era, and reuse the ready-made UiTexts.

Last updated

Steps

Every user-visible string in the API is a Text. Write it as a translation key plus an English fallback, and the menu works in English before you ship any lang file.

Two kinds of Text#

Text title = Text.translatable("mymod.title", "My Mod Settings");                 // translated
Text count = Text.translatable("mymod.count", "%s of %s enabled", enabled, total);
Text saved = Text.translatable("mymod.saved", "Saved to %s", Text.literal(fileName));
Text name  = Text.literal(profile.name());                                          // shown as-is
Factory Shown as Use it for
Text.translatable(key, fallback, args...) the running language's translation of key, or fallback when there is none everything you write yourself
Text.literal(text) the text, in every language. % is not special. what players or files provide: profile names, file names, server addresses

The fallback matters more than usual: it is what players see wherever your lang files aren't loaded at all, which includes Fabric without Fabric API. Write every fallback as finished English text.

Arguments#

The fallback (and every translation) is a format pattern, as in Minecraft's own lang files:

Choose keys#

The convention is <modid>.<what>, for example mymod.sounds. The library's own keys use the xis_mvui. prefix, so keep your keys under your own mod id.

Ship lang files#

Lang files live in your mod's assets/<modid>/lang/ folder. The format changed over Minecraft's history:

Minecraft File Format
1.13 – 26.3 en_us.json JSON object
1.11 – 1.12.2 en_us.lang key=value, one per line
1.7.10 – 1.10.2 en_US.lang key=value, one per line
assets/mymod/lang/en_us.json
{
  "mymod.title": "My Mod Settings",
  "mymod.count": "%s of %s enabled"
}
assets/mymod/lang/en_us.lang
mymod.title=My Mod Settings
mymod.count=%s of %s enabled

A mod that ships for every version can include all three files in its jars; each game version reads the one it knows. The library itself ships its strings this way.

Ready-made texts#

UiTexts has texts you can reuse. These use vanilla's own keys, so they are translated in every language with nothing shipped:

Constant English
UiTexts.DONE Done
UiTexts.CANCEL Cancel
UiTexts.YES Yes
UiTexts.NO No
UiTexts.ON ON
UiTexts.OFF OFF
.footer(Button.builder("done").label(UiTexts.DONE).onPress(host::back).build())

UiTexts also holds the texts the widgets use themselves ("Working…", "Add", "Remove", the number and color errors, the section markers ▼ and ▶). Those use xis_mvui.* keys and are translated by the library's lang files.

Test it#

Text.toEnglish() fills the arguments into the fallback, which is what FakeHost.render() prints:

assertEquals("2 of 5 enabled", Text.translatable("mymod.count", "%s of %s enabled", 2, 5).toEnglish());

Next steps#