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.
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:
- Use
%sfor each argument, or%1$s,%2$sto reorder them in a translation. - Prefer
%sto number formats like%d. Minecraft's translation only supports%sand%n$s. - Arguments may be strings, numbers, booleans or other
Texts. A nestedTextis translated too. Other objects show as theirtoString(). - Wrap player-provided strings in
Text.literal(…)when you pass them as arguments, as withfileNameabove.
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 |
{
"mymod.title": "My Mod Settings",
"mymod.count": "%s of %s enabled"
}
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#
- Your first menu: a menu with every string translatable.