From 4604aaa85b75f7626303c05c1279b64f61b63b8b Mon Sep 17 00:00:00 2001 From: Discreater Date: Tue, 13 Oct 2026 22:29:42 +0800 Subject: [PATCH] Add mixin to localize item names in chat components on the client (#480) --- .../gtnewhorizon/gtnhlib/GTNHLibConfig.java | 5 ++ .../gtnewhorizon/gtnhlib/mixins/Mixins.java | 3 ++ .../MixinItemStack_GetChatComponent.java | 46 +++++++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 src/main/java/com/gtnewhorizon/gtnhlib/mixins/early/MixinItemStack_GetChatComponent.java diff --git a/src/main/java/com/gtnewhorizon/gtnhlib/GTNHLibConfig.java b/src/main/java/com/gtnewhorizon/gtnhlib/GTNHLibConfig.java index 6d295e40..2edb00bc 100644 --- a/src/main/java/com/gtnewhorizon/gtnhlib/GTNHLibConfig.java +++ b/src/main/java/com/gtnewhorizon/gtnhlib/GTNHLibConfig.java @@ -36,6 +36,11 @@ public class GTNHLibConfig { @Config.DefaultBoolean(true) public static boolean enableTranslucentItemRenders; + @Config.Comment("Replace item names in chat components (death messages, /give feedback, NEI item linking) with GTNHLib custom components so they are localized on the client. Disable this if vanilla (non-GTNHLib) clients connect to your server, as they cannot deserialize GTNHLib custom chat components.") + @Config.DefaultBoolean(true) + @Config.RequiresMcRestart + public static boolean enableLocalizedItemChatComponents; + @Config.Comment("Enable client-side resource pack update checking for active packs") @Config.DefaultBoolean(true) public static boolean enableResourcePackUpdateCheck; diff --git a/src/main/java/com/gtnewhorizon/gtnhlib/mixins/Mixins.java b/src/main/java/com/gtnewhorizon/gtnhlib/mixins/Mixins.java index 3c2a65ae..09eb35fc 100644 --- a/src/main/java/com/gtnewhorizon/gtnhlib/mixins/Mixins.java +++ b/src/main/java/com/gtnewhorizon/gtnhlib/mixins/Mixins.java @@ -57,6 +57,9 @@ public enum Mixins implements IMixins { .setApplyIf(() -> GTNHLibConfig.enableTranslucentItemRenders)), CUSTOM_CHAT_COMPONENT_REGISTRATION(new MixinBuilder("Custom chat component registration") .addCommonMixins("MixinIChatComponentSerializer").setPhase(Phase.EARLY)), + ITEM_CHAT_COMPONENT(new MixinBuilder("Localize item names in chat components on the client") + .addCommonMixins("MixinItemStack_GetChatComponent").setPhase(Phase.EARLY) + .setApplyIf(() -> GTNHLibConfig.enableLocalizedItemChatComponents)), MULTI_RELEASE_JAR_FILTER(new MixinBuilder("Skip multi-release JAR entries in mod discovery") .addCommonMixins("fml.MixinJarDiscoverer").setPhase(Phase.EARLY).addExcludedMod(TargetMods.LWJGL3IFY)), PICK_BLOCK_TRAP(Side.CLIENT, "MixinMinecraft_PickBlockTrap"), diff --git a/src/main/java/com/gtnewhorizon/gtnhlib/mixins/early/MixinItemStack_GetChatComponent.java b/src/main/java/com/gtnewhorizon/gtnhlib/mixins/early/MixinItemStack_GetChatComponent.java new file mode 100644 index 00000000..c1e88ca3 --- /dev/null +++ b/src/main/java/com/gtnewhorizon/gtnhlib/mixins/early/MixinItemStack_GetChatComponent.java @@ -0,0 +1,46 @@ +package com.gtnewhorizon.gtnhlib.mixins.early; + +import net.minecraft.event.HoverEvent; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.IChatComponent; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import com.gtnewhorizon.gtnhlib.chat.customcomponents.ChatComponentItemName; + +/** + * Vanilla {@code ItemStack.func_151000_E()} bakes the already-translated {@code getDisplayName()} into a + * {@link ChatComponentText} before the component is sent to the client. Because that translation happens on the server, + * clients end up rendering the server's language instead of their own for death messages, {@code /give} feedback, + * statistics, etc. + * + *

+ * This mixin replaces the name text with a {@link ChatComponentItemName}, which carries the raw {@link ItemStack}. The + * stack is serialized as-is and only resolved via {@code getDisplayName()} on the client, using the client's locale. + */ +@Mixin(ItemStack.class) +public class MixinItemStack_GetChatComponent { + + @Inject(method = "func_151000_E", at = @At("HEAD"), cancellable = true) + private void gtnhlib$localizeItemNameOnClient(CallbackInfoReturnable cir) { + ItemStack stack = (ItemStack) (Object) this; + + // ChatComponentItemName already renders as "[name]" (with brackets) like vanilla func_151000_E(). + IChatComponent ichatcomponent = new ChatComponentItemName(stack); + + if (stack.getItem() != null) { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + stack.writeToNBT(nbttagcompound); + ichatcomponent.getChatStyle().setChatHoverEvent( + new HoverEvent(HoverEvent.Action.SHOW_ITEM, new ChatComponentText(nbttagcompound.toString()))); + ichatcomponent.getChatStyle().setColor(stack.getRarity().rarityColor); + } + + cir.setReturnValue(ichatcomponent); + } +}