diff --git a/src/main/java/codechicken/nei/ClientHandler.java b/src/main/java/codechicken/nei/ClientHandler.java index f5ba173f..eca7f418 100644 --- a/src/main/java/codechicken/nei/ClientHandler.java +++ b/src/main/java/codechicken/nei/ClientHandler.java @@ -1,10 +1,13 @@ package codechicken.nei; +import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; +import java.io.InputStreamReader; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; @@ -22,9 +25,11 @@ import net.minecraft.client.gui.GuiErrorScreen; import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.gui.GuiSelectWorld; import net.minecraft.client.multiplayer.WorldClient; +import net.minecraft.client.resources.IResource; import net.minecraft.entity.Entity; import net.minecraft.entity.item.EntityItem; import net.minecraft.launchwrapper.Launch; +import net.minecraft.util.ResourceLocation; import net.minecraft.world.World; import net.minecraftforge.client.event.GuiOpenEvent; import net.minecraftforge.client.event.RenderWorldLastEvent; @@ -165,11 +170,33 @@ public class ClientHandler { }, callback); } - public static void loadSettingsFile(String resource, BiConsumer createDefault, + public static boolean loadSettingsResource(String resource, Consumer> callback) { + final ResourceLocation location = new ResourceLocation("nei", resource); + + try { + final IResource res = Minecraft.getMinecraft().getResourceManager().getResource(location); + + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(res.getInputStream(), StandardCharsets.UTF_8))) { + NEIClientConfig.logger.info("Loading '{}' from jar {}", resource, location); + callback.accept( + IOUtils.readLines(reader).stream().map(String::trim) + .filter(line -> !line.isEmpty() && !line.startsWith("#"))); + return true; + } + + } catch (Exception io) { + NEIClientConfig.logger.error("Failed to load '{}' file {}", resource, location); + } + + return false; + } + + public static boolean loadSettingsFile(String resource, BiConsumer createDefault, Consumer> callback) { File file = new File(NEIClientConfig.configDir, resource); - if (!file.exists()) { + if (!file.exists() && createDefault != null) { try (FileWriter writer = new FileWriter(file)) { NEIClientConfig.logger.info("Creating default '{}' {}", resource, file); createDefault.accept(file, writer); @@ -178,13 +205,19 @@ public class ClientHandler { } } - try (FileReader reader = new FileReader(file)) { - NEIClientConfig.logger.info("Loading '{}' file {}", resource, file); - callback.accept( - IOUtils.readLines(reader).stream().filter(line -> !line.startsWith("#") && !line.trim().isEmpty())); - } catch (IOException e) { - NEIClientConfig.logger.error("Failed to load '{}' file {}", resource, file, e); + if (file.exists()) { + try (FileReader reader = new FileReader(file)) { + NEIClientConfig.logger.info("Loading '{}' file {}", resource, file); + callback.accept( + IOUtils.readLines(reader).stream().map(String::trim) + .filter(line -> !line.isEmpty() && !line.startsWith("#"))); + return true; + } catch (IOException e) { + NEIClientConfig.logger.error("Failed to load '{}' file {}", resource, file, e); + } } + + return false; } public static void loadSerialHandlers() { diff --git a/src/main/java/codechicken/nei/ItemList.java b/src/main/java/codechicken/nei/ItemList.java index e027d525..6ae8a5aa 100644 --- a/src/main/java/codechicken/nei/ItemList.java +++ b/src/main/java/codechicken/nei/ItemList.java @@ -30,6 +30,7 @@ import codechicken.nei.api.ItemFilter.ItemFilterProvider; import codechicken.nei.api.ItemInfo; import codechicken.nei.recipe.InformationHandler; import codechicken.nei.search.TooltipFilter; +import codechicken.nei.util.ItemUntranslator; public class ItemList { @@ -101,6 +102,12 @@ public class ItemList { return true; } + displayName = ItemUntranslator.getInstance().getItemStackDisplayName(item); + + if (!displayName.isEmpty() && this.pattern.matcher(displayName).find()) { + return true; + } + if (item.hasDisplayName()) { displayName = EnumChatFormatting .getTextWithoutFormattingCodes(item.getItem().getItemStackDisplayName(item)); diff --git a/src/main/java/codechicken/nei/NEIClientConfig.java b/src/main/java/codechicken/nei/NEIClientConfig.java index 058f98fb..0b8173a4 100644 --- a/src/main/java/codechicken/nei/NEIClientConfig.java +++ b/src/main/java/codechicken/nei/NEIClientConfig.java @@ -61,6 +61,7 @@ import codechicken.nei.recipe.IRecipeHandler; import codechicken.nei.recipe.InformationHandler; import codechicken.nei.recipe.RecipeCatalysts; import codechicken.nei.recipe.RecipeInfo; +import codechicken.nei.util.ItemUntranslator; import codechicken.nei.util.NEIKeyboardUtils; import codechicken.obfuscator.ObfuscationRun; @@ -424,6 +425,24 @@ public class NEIClientConfig { tag.getTag("inventory.autocrafting").setComment("Autocrafting").getBooleanValue(true); API.addOption(new OptionToggleButton("inventory.autocrafting", true)); + tag.getTag("inventory.itemUntranslator").setComment("Item Untranslator").getBooleanValue(true); + API.addOption(new OptionToggleButton("inventory.itemUntranslator", true) { + + @Override + public boolean onClick(int button) { + super.onClick(button); + + if (enableItemUntranslator()) { + ItemUntranslator.getInstance().load(); + } else { + ItemUntranslator.getInstance().unload(); + } + + return true; + } + + }); + tag.getTag("tools.handler_load_from_config").setComment("ADVANCED: Load handlers from config") .getBooleanValue(false); API.addOption(new OptionToggleButton("tools.handler_load_from_config", true) { @@ -1208,6 +1227,10 @@ public class NEIClientConfig { return getBooleanSetting("inventory.collapsibleItems.enabled"); } + public static boolean enableItemUntranslator() { + return getBooleanSetting("inventory.itemUntranslator"); + } + public static boolean getMagnetMode() { return enabledActions.contains("magnet"); } diff --git a/src/main/java/codechicken/nei/NEIController.java b/src/main/java/codechicken/nei/NEIController.java index 2b52ba77..e69bb63c 100644 --- a/src/main/java/codechicken/nei/NEIController.java +++ b/src/main/java/codechicken/nei/NEIController.java @@ -21,6 +21,7 @@ import codechicken.nei.api.INEIGuiHandler; import codechicken.nei.api.ItemInfo; import codechicken.nei.commands.CommandBookmarkAdd; import codechicken.nei.commands.CommandRecipeId; +import codechicken.nei.commands.CommandUntranslator; import codechicken.nei.guihook.GuiContainerManager; import codechicken.nei.guihook.IContainerInputHandler; import codechicken.nei.guihook.IContainerSlotClickHandler; @@ -68,6 +69,7 @@ public class NEIController implements IContainerSlotClickHandler, IContainerInpu public static void registerClientCommands() { ClientCommandHandler.instance.registerCommand(new CommandBookmarkAdd()); ClientCommandHandler.instance.registerCommand(new CommandRecipeId()); + ClientCommandHandler.instance.registerCommand(new CommandUntranslator()); } public static void updateUnlimitedItems(InventoryPlayer inventory) { diff --git a/src/main/java/codechicken/nei/SearchTokenParser.java b/src/main/java/codechicken/nei/SearchTokenParser.java index 9cabada3..9d60bb8e 100644 --- a/src/main/java/codechicken/nei/SearchTokenParser.java +++ b/src/main/java/codechicken/nei/SearchTokenParser.java @@ -122,10 +122,8 @@ public class SearchTokenParser { Language currentLanguage = Minecraft.getMinecraft().getLanguageManager().getCurrentLanguage(); if (!currentLanguage.getLanguageCode().equals(providersCache.languageCode)) { - providersCache.providers = new ArrayList<>(); - providersCache.languageCode = currentLanguage.getLanguageCode(); - - Map providers = new HashMap<>(); + final List providersList = new ArrayList<>(); + final Map providers = new HashMap<>(); for (int index = this.searchProviders.size() - 1; index >= 0; index--) { ISearchParserProvider provider = this.searchProviders.get(index); @@ -134,12 +132,14 @@ public class SearchTokenParser { if (provider.getSearchMode() == SearchMode.PREFIX && !providers.containsKey(provider.getPrefix())) { providers.put(provider.getPrefix(), provider); } else if (provider.getSearchMode() == SearchMode.ALWAYS) { - providersCache.providers.add(provider); + providersList.add(provider); } } } - providersCache.providers.addAll(providers.values()); + providersList.addAll(providers.values()); + providersCache.languageCode = currentLanguage.getLanguageCode(); + providersCache.providers = providersList; } return providersCache.providers; diff --git a/src/main/java/codechicken/nei/api/ItemInfo.java b/src/main/java/codechicken/nei/api/ItemInfo.java index 023493b9..56b3b616 100644 --- a/src/main/java/codechicken/nei/api/ItemInfo.java +++ b/src/main/java/codechicken/nei/api/ItemInfo.java @@ -65,6 +65,7 @@ import codechicken.nei.search.IdentifierFilter; import codechicken.nei.search.ModNameFilter; import codechicken.nei.search.OreDictionaryFilter; import codechicken.nei.search.TooltipFilter; +import codechicken.nei.util.ItemUntranslator; import cpw.mods.fml.common.FMLCommonHandler; import cpw.mods.fml.common.ModContainer; import cpw.mods.fml.common.registry.GameRegistry; @@ -137,6 +138,10 @@ public class ItemInfo { addSearchProviders(); RecipeItemInputHandler.load(); PresetsList.load(); + + if (NEIClientConfig.enableItemUntranslator()) { + ItemUntranslator.getInstance().load(); + } } private static void addSearchProviders() { diff --git a/src/main/java/codechicken/nei/commands/CommandUntranslator.java b/src/main/java/codechicken/nei/commands/CommandUntranslator.java new file mode 100644 index 00000000..5f0cd3b8 --- /dev/null +++ b/src/main/java/codechicken/nei/commands/CommandUntranslator.java @@ -0,0 +1,78 @@ +package codechicken.nei.commands; + +import java.io.File; + +import net.minecraft.command.CommandBase; +import net.minecraft.command.ICommandSender; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.ChatStyle; +import net.minecraft.util.EnumChatFormatting; + +import codechicken.nei.ItemList; +import codechicken.nei.NEIClientConfig; +import codechicken.nei.util.ItemUntranslator; + +public class CommandUntranslator extends CommandBase { + + @Override + public String getCommandName() { + return "untranslator"; + } + + @Override + public String getCommandUsage(ICommandSender sender) { + return "/untranslator dump OR /untranslator clear"; + } + + @Override + public int getRequiredPermissionLevel() { + return 0; + } + + @Override + public void processCommand(ICommandSender sender, String[] args) { + final String command = args.length == 0 ? null : args[0]; + + if ("dump".equals(command)) { + if (ItemList.items.isEmpty()) { + sendChatErrorMessage(sender, "nei.chat.untranslator.dump.error"); + return; + } else { + sendChatInfoMessage(sender, "nei.chat.untranslator.dump.start"); + ItemUntranslator.getInstance().generateUntranslatedNames(); + sendChatInfoMessage(sender, "nei.chat.untranslator.dump.finish"); + + if (NEIClientConfig.enableItemUntranslator()) { + ItemUntranslator.getInstance().load(); + } + } + } else if ("clear".equals(command)) { + final File file = new File(NEIClientConfig.configDir, "untranslator.cfg"); + + if (file.exists() && file.delete()) { + sendChatInfoMessage(sender, "nei.chat.untranslator.clear"); + if (NEIClientConfig.enableItemUntranslator()) { + ItemUntranslator.getInstance().load(); + } + } else { + sendChatErrorMessage(sender, "nei.chat.untranslator.clear.error"); + } + } else { + sendChatErrorMessage(sender, getCommandUsage(sender)); + } + + } + + private static void sendChatInfoMessage(ICommandSender sender, String translationKey, Object... args) { + sender.addChatMessage( + new ChatComponentTranslation(translationKey, args) + .setChatStyle(new ChatStyle().setColor(EnumChatFormatting.AQUA))); + } + + private static void sendChatErrorMessage(ICommandSender sender, String translationKey, Object... args) { + sender.addChatMessage( + new ChatComponentTranslation(translationKey, args) + .setChatStyle(new ChatStyle().setColor(EnumChatFormatting.RED))); + } + +} diff --git a/src/main/java/codechicken/nei/guihook/GuiContainerManager.java b/src/main/java/codechicken/nei/guihook/GuiContainerManager.java index 6636dafd..b79fec83 100644 --- a/src/main/java/codechicken/nei/guihook/GuiContainerManager.java +++ b/src/main/java/codechicken/nei/guihook/GuiContainerManager.java @@ -49,6 +49,7 @@ import codechicken.nei.NEIClientConfig; import codechicken.nei.NEIClientUtils; import codechicken.nei.NEIModContainer; import codechicken.nei.recipe.StackInfo; +import codechicken.nei.util.ItemUntranslator; import codechicken.nei.util.ReadableNumberConverter; import codechicken.nei.util.RenderTooltipEventHelper; @@ -60,6 +61,10 @@ public class GuiContainerManager { public void onResourceManagerReload(IResourceManager p_110549_1_) { renderingErrorItems.clear(); ItemList.loadItems.restart(); + + if (NEIClientConfig.enableItemUntranslator()) { + ItemUntranslator.getInstance().load(); + } } } @@ -590,8 +595,6 @@ public class GuiContainerManager { } } - if (!tooltip.isEmpty()) tooltip.set(0, tooltip.get(0) + GuiDraw.TOOLTIP_LINESPACE); // add space after 'title' - if (showTooltip && NEIClientConfig.getBooleanSetting("inventory.showHotkeys")) { List hotkeystips = collectHotkeyTips(mousex, mousey); @@ -605,6 +608,19 @@ public class GuiContainerManager { } } + if (showTooltip && stack != null && !tooltip.isEmpty()) { + final String secondDisplayName = ItemUntranslator.getInstance().getItemStackDisplayName(stack); + + if (!secondDisplayName.isEmpty()) { + tooltip.add(1, EnumChatFormatting.DARK_GRAY + secondDisplayName + GuiDraw.TOOLTIP_LINESPACE); + } else { + tooltip.set(0, tooltip.get(0) + GuiDraw.TOOLTIP_LINESPACE); + } + + } else if (!tooltip.isEmpty()) { + tooltip.set(0, tooltip.get(0) + GuiDraw.TOOLTIP_LINESPACE); + } + if (NEIModContainer.isGTNHLibLoaded() && !tooltip.isEmpty()) { if (RenderTooltipEventHelper.post(stack, this.window, mousex, mousey, font)) { return; diff --git a/src/main/java/codechicken/nei/util/ItemUntranslator.java b/src/main/java/codechicken/nei/util/ItemUntranslator.java new file mode 100644 index 00000000..4ec6dd9f --- /dev/null +++ b/src/main/java/codechicken/nei/util/ItemUntranslator.java @@ -0,0 +1,396 @@ +package codechicken.nei.util; + +import java.io.BufferedWriter; +import java.io.File; +import java.lang.reflect.Field; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.stream.Stream; + +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.StatCollector; +import net.minecraft.util.StringTranslate; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidContainerRegistry; +import net.minecraftforge.fluids.FluidContainerRegistry.FluidContainerData; +import net.minecraftforge.fluids.FluidRegistry; +import net.minecraftforge.fluids.FluidStack; + +import codechicken.nei.ClientHandler; +import codechicken.nei.ItemList; +import codechicken.nei.NEIClientConfig; +import codechicken.nei.SubsetWidget; +import codechicken.nei.api.API; +import codechicken.nei.recipe.StackInfo; +import cpw.mods.fml.common.FMLCommonHandler; +import cpw.mods.fml.common.registry.LanguageRegistry; +import cpw.mods.fml.relauncher.ReflectionHelper; + +public class ItemUntranslator { + + private class LanguageRegistryPatch implements AutoCloseable { + // RAII-style helper that temporarily swaps language data in + // LanguageRegistry / StringTranslate using reflection. This allows + // reading translation entries for a different language (eg. en_US) + // even when the game is using another language at runtime. + + private String secondLanguage = "en_US"; + private String language; + + private static Map modLanguageDataSymlink; + private Properties languageDataCache; + + private static Map secondLanguageListCache; + private static Map languageListCache; + + private static Field languageListField; + private static Field fieldStringTranslateInstance; + + static { + try { + final Field modLanguageDataField = ReflectionHelper + .findField(LanguageRegistry.class, "modLanguageData"); + + languageListField = ReflectionHelper.findField(StringTranslate.class, "languageList", "field_74816_c"); + fieldStringTranslateInstance = ReflectionHelper + .findField(StringTranslate.class, "instance", "field_74817_a"); + + modLanguageDataSymlink = (Map) modLanguageDataField + .get(LanguageRegistry.instance()); + languageListCache = (Map) languageListField.get(fieldStringTranslateInstance.get(null)); + } catch (Throwable e) { + languageListField = null; + fieldStringTranslateInstance = null; + } + } + + private LanguageRegistryPatch(String secondLanguage) { + this.language = FMLCommonHandler.instance().getCurrentLanguage(); + this.secondLanguage = secondLanguage; + patchLanguageRegistry(); + patchStringTranslate(); + } + + @Override + public void close() { + restoreStringTranslate(); + restoreLanguageRegistry(); + } + + private void patchLanguageRegistry() { + this.languageDataCache = modLanguageDataSymlink.get(this.language); + modLanguageDataSymlink.put(this.language, modLanguageDataSymlink.get(this.secondLanguage)); + } + + private void patchStringTranslate() { + + if (languageListField != null) { + + if (secondLanguageListCache == null) { + secondLanguageListCache = new HashMap<>(languageListCache); + modLanguageDataSymlink.get(secondLanguage) + .forEach((k, v) -> secondLanguageListCache.put((String) k, (String) v)); + } + + try { + languageListField.set(fieldStringTranslateInstance.get(null), secondLanguageListCache); + } catch (IllegalAccessException e) { + throw new RuntimeException("Failed to access StringTranslate language list", e); + } + } + } + + private void restoreLanguageRegistry() { + final String language = FMLCommonHandler.instance().getCurrentLanguage(); + modLanguageDataSymlink.put(language, languageDataCache); + languageDataCache = null; + } + + private void restoreStringTranslate() { + if (languageListField != null && languageListCache != null) { + try { + languageListField.set(fieldStringTranslateInstance.get(null), languageListCache); + } catch (IllegalAccessException e) { + throw new RuntimeException("Failed to restore StringTranslate language list", e); + } + } + } + } + + private static final String CONFIG_FILE = "untranslator.cfg"; + private static final ItemUntranslator instance = new ItemUntranslator(); + + private final Map secondNames = new HashMap<>(); + private final Map processedNames = new HashMap<>(); + + static { + API.addSubset("Untranslated", item -> instance.getItemStackDisplayName(item).isEmpty()); + } + + private ItemUntranslator() {} + + public static ItemUntranslator getInstance() { + return instance; + } + + public void generateUntranslatedNames() { + final Map items = new HashMap<>(); + + items.put("language", FMLCommonHandler.instance().getCurrentLanguage()); + + for (Fluid fluid : FluidRegistry.getRegisteredFluids().values()) { + items.put( + fluid.getUnlocalizedName().toLowerCase(), + stripFormatting(StatCollector.translateToLocal(fluid.getUnlocalizedName()))); + } + + for (CreativeTabs tab : CreativeTabs.creativeTabArray) { + final List tabItems = new ArrayList<>(); + + tab.displayAllReleventItems(tabItems); + + for (ItemStack stack : tabItems) { + collectDisplayNameForStack(stack, items); + } + } + + for (ItemStack stack : ItemList.items) { + collectDisplayNameForStack(stack, items); + } + + for (FluidContainerData data : FluidContainerRegistry.getRegisteredFluidContainerData()) { + if (data.filledContainer != null) { + collectDisplayNameForStack(data.filledContainer, items); + } + } + + for (Map.Entry entry : LanguageRegistryPatch.languageListCache.entrySet()) { + final String key = entry.getKey().toLowerCase(); + if (key.endsWith(".name") && !items.containsKey(key) && !entry.getValue().contains("%")) { + items.put(key, entry.getValue()); + } + } + + saveMappings(items); + } + + private void collectDisplayNameForStack(ItemStack stack, Map items) { + final String displayName = stripFormatting(stack.getDisplayName()); + final String guidKey = determineGUIDForGeneration(stack, displayName, items); + + if (!items.containsKey(guidKey)) { + items.put(guidKey, displayName); + } + } + + private String determineGUIDForGeneration(ItemStack stack, String displayName, Map items) { + final String unlocalizedName = stack.getUnlocalizedName().toLowerCase(); + + if (displayName.equals(stripFormatting(StatCollector.translateToLocal(unlocalizedName)))) { + return unlocalizedName; + } + + if (!unlocalizedName.endsWith(".name") + && displayName.equals(stripFormatting(StatCollector.translateToLocal(unlocalizedName + ".name")))) { + return unlocalizedName + ".name"; + } + + final FluidStack fluid = StackInfo.getFluid(stack); + + if (fluid != null) { + final String fluidUnlocalizedName = fluid.getUnlocalizedName().toLowerCase(); + + if (displayName.equals(items.get(fluidUnlocalizedName))) { + return fluidUnlocalizedName; + } + + if (!fluidUnlocalizedName.startsWith("fluid.") + && displayName.equals(items.get("fluid." + fluidUnlocalizedName))) { + return "fluid." + fluidUnlocalizedName; + } + + return (Item.itemRegistry.getNameForObject(stack.getItem()) + ":" + + stack.getItemDamage() + + "@" + + fluid.getUnlocalizedName()).toLowerCase(); + } + + if (stack.hasTagCompound()) { + try { + final ItemStack stackCopy = new ItemStack(stack.getItem(), 1, stack.getItemDamage()); + + if (displayName.equals(stripFormatting(stackCopy.getDisplayName()))) { + return (Item.itemRegistry.getNameForObject(stack.getItem()) + ":" + stack.getItemDamage()) + .toLowerCase(); + } + } catch (Exception e) {} + } + + return StackInfo.getItemStackGUID(stack); + } + + private String stripFormatting(String name) { + return EnumChatFormatting.getTextWithoutFormattingCodes(name).trim(); + } + + public synchronized String getItemStackDisplayName(ItemStack stack) { + + if (stack == null || this.secondNames.isEmpty()) { + return ""; + } + + final String guidKey = StackInfo.getItemStackGUID(stack); + + return this.processedNames.computeIfAbsent(guidKey, stackKey -> { + String displayName = this.secondNames.get(stackKey); + + if (displayName == null) { + final String guid = findBestGUIDForLookup(stack); + displayName = this.secondNames + .computeIfAbsent(guid == null ? stackKey : guid, g -> getFallbackDisplayName(stack)); + } + + return displayName.equals(stripFormatting(stack.getDisplayName())) ? "" : displayName; + }); + } + + public String findBestGUIDForLookup(ItemStack stack) { + String guidKey = (Item.itemRegistry.getNameForObject(stack.getItem()) + ":" + stack.getItemDamage()) + .toLowerCase(); + + if (this.secondNames.containsKey(guidKey)) { + return guidKey; + } + + final FluidStack fluid = StackInfo.getFluid(stack); + + if (fluid != null) { + guidKey = (Item.itemRegistry.getNameForObject(stack.getItem()) + ":" + + stack.getItemDamage() + + "@" + + fluid.getUnlocalizedName()).toLowerCase(); + + if (this.secondNames.containsKey(guidKey)) { + return guidKey; + } + + final String fluidUnlocalizedName = fluid.getUnlocalizedName().toLowerCase(); + + if (!fluidUnlocalizedName.startsWith("fluid.") + && this.secondNames.containsKey("fluid." + fluidUnlocalizedName)) { + return "fluid." + fluidUnlocalizedName; + } + + if (this.secondNames.containsKey(fluidUnlocalizedName)) { + return fluidUnlocalizedName; + } + } + + final String unlocalizedName = stack.getUnlocalizedName().toLowerCase(); + + if (!unlocalizedName.endsWith(".name") && this.secondNames.containsKey(unlocalizedName + ".name")) { + return unlocalizedName + ".name"; + } + + if (this.secondNames.containsKey(unlocalizedName)) { + return unlocalizedName; + } + + return null; + } + + private String getFallbackDisplayName(ItemStack stack) { + final String unlocalizedName = stack.getUnlocalizedName(); + final String secondLanguage = this.secondNames.get("language"); + String name = LanguageRegistry.instance().getStringLocalization(unlocalizedName, secondLanguage); + + if (name == null && !unlocalizedName.endsWith(".name")) { + name = LanguageRegistry.instance().getStringLocalization(unlocalizedName + ".name", secondLanguage); + } + + if (name == null || name.contains("%")) { + try (LanguageRegistryPatch lrPatch = new LanguageRegistryPatch(secondLanguage)) { + name = stack.getDisplayName(); + } + } + + final FluidStack fluid = StackInfo.getFluid(stack); + + if (fluid != null) { + final String unlocalizedFluidName = fluid.getUnlocalizedName().toLowerCase(); + String fluidName = this.secondNames.get(unlocalizedFluidName); + + if (fluidName == null && !unlocalizedFluidName.startsWith("fluid.")) { + fluidName = this.secondNames.get("fluid." + unlocalizedFluidName); + } + + if (fluidName != null && name != null && !name.isEmpty()) { + return name.replace(fluid.getLocalizedName(), fluidName); + } else { + name = fluidName; + } + } + + return name == null ? "" : name; + } + + protected void saveMappings(Map items) { + try (BufferedWriter writer = Files.newBufferedWriter( + (new File(NEIClientConfig.configDir, CONFIG_FILE)).toPath(), + StandardCharsets.UTF_8)) { + for (Map.Entry entry : items.entrySet()) { + if (entry.getValue().isEmpty()) continue; + + writer.write(entry.getKey() + "=" + entry.getValue()); + writer.newLine(); + } + } catch (Exception e) { + NEIClientConfig.logger.error("Error saving untranslator", e); + } + + NEIClientConfig.logger.info("Untranslator saved"); + } + + public void unload() { + this.secondNames.clear(); + LanguageRegistryPatch.secondLanguageListCache = null; + this.processedNames.clear(); + } + + public void load() { + unload(); + + this.secondNames.clear(); + + if (ClientHandler.loadSettingsFile(CONFIG_FILE, null, this::parseStream) + || ClientHandler.loadSettingsResource(CONFIG_FILE, this::parseStream)) { + NEIClientConfig.logger.info("Untranslator loaded"); + } + + if (!this.secondNames.containsKey("language") + || FMLCommonHandler.instance().getCurrentLanguage().equals(this.secondNames.get("language"))) { + this.secondNames.clear(); + } + + SubsetWidget.updateHiddenItems(); + } + + private void parseStream(Stream lines) { + lines.forEach(line -> { + final int sepIndex = line.indexOf('='); + + if (sepIndex != -1) { + this.secondNames.put(line.substring(0, sepIndex).trim(), line.substring(sepIndex + 1).trim()); + } + }); + } + +} diff --git a/src/main/resources/assets/nei/lang/en_US.lang b/src/main/resources/assets/nei/lang/en_US.lang index 0ead8e70..a02ea3cf 100644 --- a/src/main/resources/assets/nei/lang/en_US.lang +++ b/src/main/resources/assets/nei/lang/en_US.lang @@ -149,6 +149,12 @@ nei.chat.recipeid.diff.finish=Finished processing recipe diff! nei.chat.recipeid.dump.start=Start processing recipe handlers! nei.chat.recipeid.dump.finish=Finished processing recipe handlers! +nei.chat.untranslator.dump.start=Start processing untranslator dump! +nei.chat.untranslator.dump.finish=Finished processing untranslator dump! +nei.chat.untranslator.dump.error=First, wait for the items to load! +nei.chat.untranslator.clear=File successfully deleted! +nei.chat.untranslator.clear.error=Could not delete file! + nei.options=NEI Options nei.options.back=Back nei.options.save=Save @@ -429,6 +435,9 @@ nei.options.inventory.itemzoom.showName=Display Name nei.options.inventory.itemzoom.showName.true=Show nei.options.inventory.itemzoom.showName.false=Hide nei.options.inventory.itemzoom.nameColor=Display Name Color +nei.options.inventory.itemUntranslator=Item Untranslator +nei.options.inventory.itemUntranslator.true=Enabled +nei.options.inventory.itemUntranslator.false=Disabled nei.options.command=Commands nei.options.command.creative=Gamemode