MultiLoader 26.1+ · Part 9

Data Generation: Language Files (MultiLoader 26.1+)

BEGINNER MULTILOADER 26.1-26.1.2 10 min read · Jun 14, 2026
MultiLoader 26.1+ · 17 parts
1 Getting Started with MultiLoader 26.1+ 2 Setting Up RegistrationUtils (MultiLoader 26.1+) 3 Creating Items (MultiLoader 26.1+) 4 Creating Blocks (MultiLoader 26.1+) 5 Data Generation: Block & Item Models (MultiLoader 26.1+) 6 Data Generation: Block Loot Tables (MultiLoader 26.1+) 7 Data Generation: Crafting Recipes (MultiLoader 26.1+) 8 Data Generation: Block & Item Tags (MultiLoader 26.1+) 9 Data Generation: Language Files (MultiLoader 26.1+) 10 Data Generation: Advancements (MultiLoader 26.1+) 11 Data Generation: Sound Definitions (MultiLoader 26.1+) 12 Data Generation: Particle Descriptions (MultiLoader 26.1+) 13 Data Generation: Enchantments (MultiLoader 26.1+) 14 Custom Food Items (MultiLoader 26.1+) 15 Custom Tools (MultiLoader 26.1+) 16 Custom Armour (MultiLoader 26.1+) 17 Block Entities (MultiLoader 26.1+)

So far the English language file for our mod has been hand-written JSON. Like models and blockstates, translation keys can be generated automatically using datagen. Switching to a LanguageProvider gives you type-safe references to your registered objects rather than hand-coded string keys, which makes it easy to stay in sync as your registry grows.

NOTE
Complete Data Generation: Block & Item Models before continuing. We add a new provider to the existing gatherClientData listener.

Language Provider

In your NeoForge data package, create a class called ExampleLanguageProvider extending LanguageProvider:

java
public class ExampleLanguageProvider extends LanguageProvider {
public ExampleLanguageProvider(PackOutput output) {
super(output, Constants.MOD_ID, "en_us");
}
@Override
protected void addTranslations() {
add(ItemRegistry.IRON_STICK.get(), "Iron Stick");
add(BlockRegistry.NEW_DIRT.get(), "New Dirt");
add("itemGroup." + Constants.MOD_ID + ".tab", "Example Mod Items");
add("itemGroup." + Constants.MOD_ID + ".blocks_tab", "Example Mod Blocks");
}
}

The constructor passes your mod ID and the locale code ("en_us") to the parent. To support additional languages, create a second subclass with a different locale string and register it the same way.

add(Block, String) and add(Item, String) automatically derive the correct translation key from the registry object, so you never have to write "block.examplemod.new_dirt" by hand. For keys that have no typed helper, such as creative tab titles, use the raw add(String key, String value) overload.

TIP
Other typed overloads cover entities, effects, biomes, and dimensions. Check the LanguageProvider source for the full list. If a registry object does not have a dedicated overload, fall back to add(registryObject.get().getDescriptionId(), "Name").

Registering the Provider

Language files are client-side assets, so the provider goes into the gatherClientData listener alongside the model provider:

java
public static void gatherClientData(GatherDataEvent.Client event) {
event.createProvider(output -> new ExampleModelProvider(output));
event.createProvider(ExampleLanguageProvider::new);
}

Because ExampleLanguageProvider has a single-argument constructor that takes PackOutput, it can be passed as a constructor reference directly. For providers whose constructors take additional arguments, use a lambda as shown for the model provider.

Running Datagen

Before running, delete the hand-written src/main/resources/assets/examplemod/lang/en_us.json from the common project so the generated file does not conflict with it. Then run the NeoForge Data configuration. Afterwards you will find:

  • assets/examplemod/lang/en_us.json inside the generated resources folder
json
{
"item.examplemod.iron_stick": "Iron Stick",
"block.examplemod.new_dirt": "New Dirt",
"item.examplemod.new_dirt": "New Dirt",
"itemGroup.examplemod.tab": "Example Mod Items",
"itemGroup.examplemod.blocks_tab": "Example Mod Blocks"
}
TIP
Notice the generator produces both item.examplemod.new_dirt and block.examplemod.new_dirt when you call add(Block, String). Both keys are needed because the block and its item each have their own description ID.

You can find the source for this tutorial here:

View Source on GitHub
NEXT IN SERIES

Data Generation: Advancements (MultiLoader 26.1+)

Build a small advancement tree using AdvancementProvider and AdvancementSubProvider, define a root node and a child advancement with InventoryChangeTrigger criteria, and generate the JSON via datagen.

Continue →