MultiLoader 1.21+ · Part 18

Data Generation: Language Files (MultiLoader 1.21+)

BEGINNER MULTILOADER 1.21-1.21.1 12 min read · Sep 7, 2026
MultiLoader 1.21+ · 28 parts
1 Getting Started with MultiLoader 1.21+ 2 Setting Up RegistrationUtils 3 Creating Items (MultiLoader 1.21+) 4 Creating Blocks (MultiLoader 1.21+) 5 Data Generation: Block & Item Models (MultiLoader 1.21+) 6 Data Generation: Block Loot Tables (MultiLoader 1.21+) 7 Data Generation: Crafting Recipes (MultiLoader 1.21+) 8 Data Generation: Block & Item Tags (MultiLoader 1.21+) 9 Custom Food Items (MultiLoader 1.21+) 10 Custom Tools (MultiLoader 1.21+) 11 Custom Armour (MultiLoader 1.21+) 12 Block Entities (MultiLoader 1.21+) 13 Config Files (MultiLoader 1.21+) 14 Custom Sounds (MultiLoader 1.21+) 15 Events and Listeners (MultiLoader 1.21+) 16 Networking and Custom Packets (MultiLoader 1.21+) 17 Data Generation: Advancements (MultiLoader 1.21+) 18 Data Generation: Language Files (MultiLoader 1.21+) 19 Custom Entities (MultiLoader 1.21+) 20 Ore Generation (MultiLoader 1.21+) 21 Introduction to Mixins (MultiLoader 1.21+) 22 Custom Particles (MultiLoader 1.21+) 23 Menus & Screens (MultiLoader 1.21+) 24 Key Bindings (MultiLoader 1.21+) 25 Custom Potion Effects (MultiLoader 1.21+) 26 Custom Enchantments (MultiLoader 1.21+) 27 Custom Commands (MultiLoader 1.21+) 28 Custom Biomes (MultiLoader 1.21+)

Until now the mod's translation strings have lived in a hand-written en_us.json file. This tutorial replaces that file with a NeoForge LanguageProvider so all translation keys are generated - keeping them consistent with your Java registries and catching typos at compile time.

NOTE
Complete the Data Generation: Advancements tutorial before continuing. We add a new provider to the existing gatherData method.

Delete the Existing Lang File

Before running datagen you must remove the hand-written lang file so the generated one is not merged with stale entries. Delete:

text
common/src/main/resources/assets/examplemod/lang/en_us.json

Datagen will write a fresh copy to the generated resources directory, which the build already places on the resource-pack lookup path.

Language Provider

In your NeoForge data package, create ExampleLangProvider extending LanguageProvider. The superclass constructor takes the pack output, your mod ID, and the target locale:

java
public class ExampleLangProvider extends LanguageProvider {
public ExampleLangProvider(PackOutput output) {
super(output, Constants.MOD_ID, "en_us");
}
@Override
protected void addTranslations() {
// translation entries go here
}
}

The locale string ("en_us") determines the output file name. Datagen writes the result to assets/<modid>/lang/<locale>.json.

Adding Translations

LanguageProvider ships typed helpers for the most common registry objects. The addItem and addBlock variants accept a Supplier, so you can pass your DeferredHolder references directly - no .get() needed. For keys that have no dedicated helper (creative tabs, sound subtitles, advancement text) use the generic add(String key, String value) overload:

java
@Override
protected void addTranslations() {
// Items - addItem(Supplier<? extends Item>, String)
addItem(ItemRegistry.IRON_STICK, "Iron Stick");
addItem(ItemRegistry.TOMATO, "Tomato");
addItem(ItemRegistry.EXAMPLE_SWORD, "Example Sword");
addItem(ItemRegistry.EXAMPLE_PICKAXE, "Example Pickaxe");
addItem(ItemRegistry.EXAMPLE_AXE, "Example Axe");
addItem(ItemRegistry.EXAMPLE_SHOVEL, "Example Shovel");
addItem(ItemRegistry.EXAMPLE_HOE, "Example Hoe");
addItem(ItemRegistry.EXAMPLE_HELMET, "Example Helmet");
addItem(ItemRegistry.EXAMPLE_CHESTPLATE, "Example Chestplate");
addItem(ItemRegistry.EXAMPLE_LEGGINGS, "Example Leggings");
addItem(ItemRegistry.EXAMPLE_BOOTS, "Example Boots");
// Blocks - addBlock(Supplier<? extends Block>, String)
addBlock(BlockRegistry.NEW_DIRT, "New Dirt");
addBlock(BlockRegistry.EXAMPLE_BE_BLOCK, "Example Block Entity Block");
// Creative tabs, subtitles, advancements - no dedicated helper, use add()
add("itemGroup.examplemod.items", "Example Mod Items");
add("itemGroup.examplemod.blocks", "Example Mod Blocks");
add("subtitles.examplemod.example_ambient", "Example ambient sound");
add("advancements.examplemod.root.title", "Example Mod");
add("advancements.examplemod.root.description", "Begin your journey with Example Mod.");
add("advancements.examplemod.get_iron_stick.title", "A New Tool");
add("advancements.examplemod.get_iron_stick.description", "Pick up an Iron Stick.");
add("advancements.examplemod.place_new_dirt.title", "Breaking Ground");
add("advancements.examplemod.place_new_dirt.description", "Place a New Dirt block.");
}
TIP
Other typed helpers include addEntityType(Supplier<EntityType<?>>, String), addEnchantment(Supplier<Enchantment>, String), and addEffect(Supplier<MobEffect>, String). Each resolves the registry key for you so the translation key always stays in sync with the registered name.

Multiple Languages

To ship more than one language, create a separate LanguageProvider subclass per locale and register each one independently. Only the locale string passed to super() changes:

java
public class ExampleLangProviderFrench extends LanguageProvider {
public ExampleLangProviderFrench(PackOutput output) {
super(output, Constants.MOD_ID, "fr_fr");
}
@Override
protected void addTranslations() {
addItem(ItemRegistry.IRON_STICK, "Bâton de fer");
addBlock(BlockRegistry.NEW_DIRT, "Nouvelle terre");
// ... remaining entries
}
}

Each provider writes its own independent JSON file. You do not need to repeat keys that Minecraft would fall back to - any key missing from a non-English file falls back to en_us automatically in vanilla.

NOTE
For this tutorial series we only generate en_us. The French class above is shown for illustration only - you don't need to create it.

Registering the Provider

Language files are client-side assets, so use event.includeClient() rather than event.includeServer():

java
@SubscribeEvent
public static void gatherData(GatherDataEvent event) {
DataGenerator generator = event.getGenerator();
PackOutput output = generator.getPackOutput();
// ... existing providers ...
generator.addProvider(
event.includeClient(),
new ExampleLangProvider(output));
}

Running Datagen

Run NeoForge Data. The generated file appears at:

text
common/src/generated/resources/assets/examplemod/lang/en_us.json

Open it and verify every key from the deleted hand-written file is present. Launch the game and confirm item names, block names, creative tab labels, and advancement text all display correctly.

TIP
If you see untranslated keys in-game (raw strings like item.examplemod.iron_stick) it usually means either the generated file wasn't included in the resource pack path or the datagen run failed silently - check the run console for errors.

You can find the source for this tutorial here:

View Source on GitHub
NEXT IN SERIES

Custom Entities (MultiLoader 1.21+)

Register an EntityType, create a PathfinderMob subclass with wandering and look-at goals, wire up attribute registration on both loaders, add a client-only renderer using a vanilla model, register a colour-tinted spawn egg, and generate entity loot tables.

Continue →