# 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.

URL: https://xichil.dev/docs/xis-mvui/v1/guides/translations/ · Updated: 2026-11-06 · Applies to: Fabric, Quilt, Legacy Fabric, Forge, NeoForge, MC 1.7.10 - 26.3

Every user-visible string in the API is a [Text](https://xichil.dev/javadoc/xis-mvui/v1/dev/xichil/mvui/Text.html). 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

```java
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 `%s` for each argument, or `%1$s`, `%2$s` to reorder them in a translation.
- Prefer `%s` to number formats like `%d`. Minecraft's translation only supports `%s` and `%n$s`.
- Arguments may be strings, numbers, booleans or other `Text`s. A nested `Text` is translated too. Other objects
  show as their `toString()`.
- Wrap player-provided strings in `Text.literal(…)` when you pass them as arguments, as with `fileName` above.

## 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 |

```json title="assets/mymod/lang/en_us.json"
{
  "mymod.title": "My Mod Settings",
  "mymod.count": "%s of %s enabled"
}
```

```properties title="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.

> **Warning:** `en_us.lang` and `en_US.lang` differ only in case, so they can't sit side by side in a source folder on Windows or
> on macOS's default file system. Keep one in your sources and have the build write the other into the jar (the
> library generates its lang files at build time). Wherever a file is missing, players see your English fallbacks.

## Ready-made texts

[UiTexts](https://xichil.dev/javadoc/xis-mvui/v1/dev/xichil/mvui/UiTexts.html) 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 |

```java
.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:

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

## Next steps

- [Your first menu](https://xichil.dev/docs/xis-mvui/v1/getting-started/first-menu/index.md): a menu with every string translatable.
