FabricLanguageProvider - Data Generation API (#2451)

Co-authored-by: modmuss50 <modmuss50@gmail.com>
Co-authored-by: apple502j <33279053+apple502j@users.noreply.github.com>
This commit is contained in:
mineblock11 2022-09-25 14:44:35 +01:00 committed by GitHub
parent a37366baa3
commit 0b73465810
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 282 additions and 1 deletions

View file

@ -5,7 +5,8 @@ moduleDependencies(project, [
'fabric-api-base', 'fabric-api-base',
'fabric-registry-sync-v0', 'fabric-registry-sync-v0',
'fabric-networking-api-v1', 'fabric-networking-api-v1',
'fabric-resource-conditions-api-v1' 'fabric-resource-conditions-api-v1',
'fabric-item-groups-v0'
]) ])
dependencies { dependencies {

View file

@ -0,0 +1,214 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.api.datagen.v1.provider;
import java.io.IOException;
import java.io.Reader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.jetbrains.annotations.ApiStatus;
import net.minecraft.block.Block;
import net.minecraft.data.DataProvider;
import net.minecraft.data.DataWriter;
import net.minecraft.enchantment.Enchantment;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.attribute.EntityAttribute;
import net.minecraft.entity.effect.StatusEffect;
import net.minecraft.item.Item;
import net.minecraft.item.ItemGroup;
import net.minecraft.stat.StatType;
import net.minecraft.util.Identifier;
import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator;
/**
* Extend this class and implement {@link FabricLanguageProvider#generateTranslations(TranslationBuilder)}.
* Make sure to use {@link FabricLanguageProvider#FabricLanguageProvider(FabricDataGenerator, String)} FabricLanguageProvider} to declare what language code is being generated if it isn't {@code en_us}.
*
* <p>Register an instance of the class with {@link FabricDataGenerator#addProvider} in a {@link net.fabricmc.fabric.api.datagen.v1.DataGeneratorEntrypoint}
*/
public abstract class FabricLanguageProvider implements DataProvider {
protected final FabricDataGenerator dataGenerator;
private final String languageCode;
protected FabricLanguageProvider(FabricDataGenerator dataGenerator) {
this(dataGenerator, "en_us");
}
protected FabricLanguageProvider(FabricDataGenerator dataGenerator, String languageCode) {
this.dataGenerator = dataGenerator;
this.languageCode = languageCode;
}
/**
* Implement this method to register languages.
*
* <p>Call {@link TranslationBuilder#add(String, String)} to add a translation.
*/
public abstract void generateTranslations(TranslationBuilder translationBuilder);
@Override
public void run(DataWriter writer) throws IOException {
TreeMap<String, String> translationEntries = new TreeMap<>();
generateTranslations((String key, String value) -> {
Objects.requireNonNull(key);
Objects.requireNonNull(value);
if (translationEntries.containsKey(key)) {
throw new RuntimeException("Existing translation key found - " + key + " - Duplicate will be ignored.");
}
translationEntries.put(key, value);
});
JsonObject langEntryJson = new JsonObject();
for (Map.Entry<String, String> entry : translationEntries.entrySet()) {
langEntryJson.addProperty(entry.getKey(), entry.getValue());
}
DataProvider.writeToPath(writer, langEntryJson, getLangFilePath(this.languageCode));
}
private Path getLangFilePath(String code) {
return dataGenerator.getOutput().resolve("assets/%s/lang/%s.json".formatted(dataGenerator.getModId(), code));
}
@Override
public String getName() {
return "Language";
}
/**
* A consumer used by {@link FabricLanguageProvider#generateTranslations(TranslationBuilder)}.
*/
@ApiStatus.NonExtendable
@FunctionalInterface
public interface TranslationBuilder {
/**
* Adds a translation.
*
* @param translationKey The key of the translation.
* @param value The value of the entry.
*/
void add(String translationKey, String value);
/**
* Adds a translation for an {@link Item}.
* @param item The {@link Item} to get the translation key from.
* @param value The value of the entry.
*/
default void add(Item item, String value) {
add(item.getTranslationKey(), value);
};
/**
* Adds a translation for a {@link Block}.
* @param block The {@link Block} to get the translation key from.
* @param value The value of the entry.
*/
default void add(Block block, String value) {
add(block.getTranslationKey(), value);
}
/**
* Adds a translation for an {@link ItemGroup}.
* @param group The {@link ItemGroup} to get the translation key from.
* @param value The value of the entry.
*/
default void add(ItemGroup group, String value) {
add("itemGroup." + group.getName(), value);
}
/**
* Adds a translation for an {@link EntityType}.
* @param entityType The {@link EntityType} to get the translation key from.
* @param value The value of the entry.
*/
default void add(EntityType<?> entityType, String value) {
add(entityType.getTranslationKey(), value);
}
/**
* Adds a translation for an {@link Enchantment}.
* @param enchantment The {@link Enchantment} to get the translation key from.
* @param value The value of the entry.
*/
default void add(Enchantment enchantment, String value) {
add(enchantment.getTranslationKey(), value);
}
/**
* Adds a translation for an {@link EntityAttribute}.
* @param entityAttribute The {@link EntityAttribute} to get the translation key from.
* @param value The value of the entry.
*/
default void add(EntityAttribute entityAttribute, String value) {
add(entityAttribute.getTranslationKey(), value);
}
/**
* Adds a translation for a {@link StatType}.
* @param statType The {@link StatType} to get the translation key from.
* @param value The value of the entry.
*/
default void add(StatType<?> statType, String value) {
add(statType.getTranslationKey(), value);
}
/**
* Adds a translation for a {@link StatusEffect}.
* @param statusEffect The {@link StatusEffect} to get the translation key from.
* @param value The value of the entry.
*/
default void add(StatusEffect statusEffect, String value) {
add(statusEffect.getTranslationKey(), value);
}
/**
* Adds a translation for an {@link Identifier}.
* @param identifier The {@link Identifier} to get the translation key from.
* @param value The value of the entry.
*/
default void add(Identifier identifier, String value) {
add(identifier.toTranslationKey(), value);
}
/**
* Merges an existing language file into the generated language file.
* @param existingLanguageFile The path to the existing language file.
* @throws IOException If loading the language file failed.
*/
default void add(Path existingLanguageFile) throws IOException {
try (Reader reader = Files.newBufferedReader(existingLanguageFile)) {
JsonObject translations = JsonParser.parseReader(reader).getAsJsonObject();
for (String key : translations.keySet()) {
add(key, translations.get(key).getAsString());
}
}
}
}
}

View file

@ -24,10 +24,13 @@ import net.minecraft.block.Material;
import net.minecraft.item.BlockItem; import net.minecraft.item.BlockItem;
import net.minecraft.item.Item; import net.minecraft.item.Item;
import net.minecraft.item.ItemGroup; import net.minecraft.item.ItemGroup;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.registry.Registry; import net.minecraft.util.registry.Registry;
import net.fabricmc.api.ModInitializer; import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.client.itemgroup.FabricItemGroupBuilder;
public class DataGeneratorTestContent implements ModInitializer { public class DataGeneratorTestContent implements ModInitializer {
public static final String MOD_ID = "fabric-data-gen-api-v1-testmod"; public static final String MOD_ID = "fabric-data-gen-api-v1-testmod";
@ -35,9 +38,11 @@ public class DataGeneratorTestContent implements ModInitializer {
public static Block SIMPLE_BLOCK; public static Block SIMPLE_BLOCK;
public static Block BLOCK_WITHOUT_ITEM; public static Block BLOCK_WITHOUT_ITEM;
public static Block BLOCK_WITHOUT_LOOT_TABLE; public static Block BLOCK_WITHOUT_LOOT_TABLE;
public static ItemGroup SIMPLE_ITEM_GROUP;
@Override @Override
public void onInitialize() { public void onInitialize() {
SIMPLE_ITEM_GROUP = FabricItemGroupBuilder.build(new Identifier(MOD_ID, "default"), () -> new ItemStack(Items.BONE));
SIMPLE_BLOCK = createBlock("simple_block", true); SIMPLE_BLOCK = createBlock("simple_block", true);
BLOCK_WITHOUT_ITEM = createBlock("block_without_item", false); BLOCK_WITHOUT_ITEM = createBlock("block_without_item", false);
BLOCK_WITHOUT_LOOT_TABLE = createBlock("block_without_loot_table", false); BLOCK_WITHOUT_LOOT_TABLE = createBlock("block_without_loot_table", false);

View file

@ -20,10 +20,17 @@ import static net.fabricmc.fabric.test.datagen.DataGeneratorTestContent.BLOCK_WI
import static net.fabricmc.fabric.test.datagen.DataGeneratorTestContent.BLOCK_WITHOUT_LOOT_TABLE; import static net.fabricmc.fabric.test.datagen.DataGeneratorTestContent.BLOCK_WITHOUT_LOOT_TABLE;
import static net.fabricmc.fabric.test.datagen.DataGeneratorTestContent.MOD_ID; import static net.fabricmc.fabric.test.datagen.DataGeneratorTestContent.MOD_ID;
import static net.fabricmc.fabric.test.datagen.DataGeneratorTestContent.SIMPLE_BLOCK; import static net.fabricmc.fabric.test.datagen.DataGeneratorTestContent.SIMPLE_BLOCK;
import static net.fabricmc.fabric.test.datagen.DataGeneratorTestContent.SIMPLE_ITEM_GROUP;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Optional;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.Consumer; import java.util.function.Consumer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.minecraft.advancement.Advancement; import net.minecraft.advancement.Advancement;
import net.minecraft.advancement.AdvancementFrame; import net.minecraft.advancement.AdvancementFrame;
import net.minecraft.advancement.criterion.OnKilledCriterion; import net.minecraft.advancement.criterion.OnKilledCriterion;
@ -31,6 +38,8 @@ import net.minecraft.data.client.BlockStateModelGenerator;
import net.minecraft.data.client.ItemModelGenerator; import net.minecraft.data.client.ItemModelGenerator;
import net.minecraft.data.server.recipe.RecipeJsonProvider; import net.minecraft.data.server.recipe.RecipeJsonProvider;
import net.minecraft.data.server.recipe.ShapelessRecipeJsonBuilder; import net.minecraft.data.server.recipe.ShapelessRecipeJsonBuilder;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.attribute.EntityAttributes;
import net.minecraft.item.Items; import net.minecraft.item.Items;
import net.minecraft.loot.LootPool; import net.minecraft.loot.LootPool;
import net.minecraft.loot.LootTable; import net.minecraft.loot.LootTable;
@ -52,6 +61,7 @@ import net.fabricmc.fabric.api.datagen.v1.DataGeneratorEntrypoint;
import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator; import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricAdvancementProvider; import net.fabricmc.fabric.api.datagen.v1.provider.FabricAdvancementProvider;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricBlockLootTableProvider; import net.fabricmc.fabric.api.datagen.v1.provider.FabricBlockLootTableProvider;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricLanguageProvider;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricModelProvider; import net.fabricmc.fabric.api.datagen.v1.provider.FabricModelProvider;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider; import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricTagProvider; import net.fabricmc.fabric.api.datagen.v1.provider.FabricTagProvider;
@ -60,6 +70,7 @@ import net.fabricmc.fabric.api.resource.conditions.v1.ConditionJsonProvider;
import net.fabricmc.fabric.api.resource.conditions.v1.DefaultResourceConditions; import net.fabricmc.fabric.api.resource.conditions.v1.DefaultResourceConditions;
public class DataGeneratorTestEntrypoint implements DataGeneratorEntrypoint { public class DataGeneratorTestEntrypoint implements DataGeneratorEntrypoint {
private static final Logger LOGGER = LoggerFactory.getLogger(DataGeneratorTestEntrypoint.class);
private static final ConditionJsonProvider NEVER_LOADED = DefaultResourceConditions.allModsLoaded("a"); private static final ConditionJsonProvider NEVER_LOADED = DefaultResourceConditions.allModsLoaded("a");
private static final ConditionJsonProvider ALWAYS_LOADED = DefaultResourceConditions.not(NEVER_LOADED); private static final ConditionJsonProvider ALWAYS_LOADED = DefaultResourceConditions.not(NEVER_LOADED);
@ -71,6 +82,8 @@ public class DataGeneratorTestEntrypoint implements DataGeneratorEntrypoint {
dataGenerator.addProvider(TestAdvancementProvider::new); dataGenerator.addProvider(TestAdvancementProvider::new);
dataGenerator.addProvider(TestBlockLootTableProvider::new); dataGenerator.addProvider(TestBlockLootTableProvider::new);
dataGenerator.addProvider(TestBarterLootTableProvider::new); dataGenerator.addProvider(TestBarterLootTableProvider::new);
dataGenerator.addProvider(ExistingEnglishLangProvider::new);
dataGenerator.addProvider(JapaneseLangProvider::new);
TestBlockTagProvider blockTagProvider = dataGenerator.addProvider(TestBlockTagProvider::new); TestBlockTagProvider blockTagProvider = dataGenerator.addProvider(TestBlockTagProvider::new);
dataGenerator.addProvider(new TestItemTagProvider(dataGenerator, blockTagProvider)); dataGenerator.addProvider(new TestItemTagProvider(dataGenerator, blockTagProvider));
@ -110,6 +123,51 @@ public class DataGeneratorTestEntrypoint implements DataGeneratorEntrypoint {
} }
} }
private static class ExistingEnglishLangProvider extends FabricLanguageProvider {
private ExistingEnglishLangProvider(FabricDataGenerator dataGenerator) {
super(dataGenerator);
}
@Override
public void generateTranslations(TranslationBuilder translationBuilder) {
translationBuilder.add(SIMPLE_BLOCK, "Simple Block");
translationBuilder.add(new Identifier(MOD_ID, "identifier_test"), "Identifier Test");
translationBuilder.add(EntityType.ALLAY, "Allay");
translationBuilder.add(EntityAttributes.GENERIC_ARMOR, "Generic Armor");
try {
Optional<Path> path = dataGenerator.getModContainer().findPath("assets/testmod/lang/en_us.base.json");
if (path.isPresent()) {
translationBuilder.add(path.get());
} else {
throw new RuntimeException("The existing language file could not be found in the testmod assets!");
}
} catch (IOException e) {
throw new RuntimeException(e);
}
try {
translationBuilder.add(EntityType.ALLAY, "Allay Duplicate Test");
} catch (RuntimeException e) {
LOGGER.info("Duplicate test passed.");
}
}
}
private static class JapaneseLangProvider extends FabricLanguageProvider {
private JapaneseLangProvider(FabricDataGenerator dataGenerator) {
super(dataGenerator, "ja_jp");
}
@Override
public void generateTranslations(TranslationBuilder translationBuilder) {
translationBuilder.add(SIMPLE_BLOCK, "シンプルブロック");
translationBuilder.add(SIMPLE_ITEM_GROUP, "データ生成項目");
translationBuilder.add("this.is.a.test", "こんにちは");
}
}
private static class TestConditionalRecipeProvider extends FabricRecipeProvider { private static class TestConditionalRecipeProvider extends FabricRecipeProvider {
private TestConditionalRecipeProvider(FabricDataGenerator dataGenerator) { private TestConditionalRecipeProvider(FabricDataGenerator dataGenerator) {
super(dataGenerator); super(dataGenerator);

View file

@ -0,0 +1,3 @@
{
"itemGroup.fabric-data-gen-api-v1-testmod.default": "Datagen Itemgroup"
}