NichtStudioCode / InvUI

A spigot library for creating custom inventory-based GUIs.
MIT License
253 stars 20 forks source link

Translation color issue #28

Closed D0gmaDev closed 1 year ago

D0gmaDev commented 1 year ago

Hi, while changing my String litterals to translated components, I faced this unexpected behaviour regarding the translation of custom colors, as they appear wrongly (InvUI 1.0 & Spigot 1.19)

Here is my test code:

String coloredString1 = net.md_5.bungee.api.ChatColor.DARK_PURPLE + "purple";
String coloredString2 = net.md_5.bungee.api.ChatColor.of("#4e6654") + "colored";
String coloredString3 = "§7gray";

Languages.getInstance().addLanguage("test-lang", Map.of("1", coloredString1, "2", coloredString2, "3", coloredString3));
Languages.getInstance().setLanguageProvider(p -> "test-lang");

player.sendMessage(coloredString1, coloredString2, coloredString3);
player.sendMessage(Languages.getInstance().getFormatString("test-lang", "1"),
   Languages.getInstance().getFormatString("test-lang", "2"),
   Languages.getInstance().getFormatString("test-lang", "3")
);

Item testItem = new SimpleItem(new ItemBuilder(Material.BOOK).addLoreLines(coloredString1, coloredString2, coloredString3));
Item translateItem = new SimpleItem(new ItemBuilder(Material.BOOK).addLoreLines(
   new BaseComponent[]{new TranslatableComponent("1")},
   new BaseComponent[]{new TranslatableComponent("2")},
   new BaseComponent[]{new TranslatableComponent("3")}
));

Gui g = Gui.normal().setStructure("..a...b..").addIngredient('a', testItem).addIngredient('b', translateItem).build();
Window.single().setTitle("TITLE").setGui(g).open(player);

I expected to get two similar items, but this is the result:

2023-04-11_18 51 37 2023-04-11_18 51 38

In this example it appears red, but for other custom colors it might appear white or any other color. However, the two sendMessage() produce expected results. Thank you for your help

NichtStudioCode commented 1 year ago

Hi, this happens because the legacy color codes don't actually support hex colors. Bukkit has implemented their own logic for creating chat components from such strings, which is why it works in sendMessage and similar methods from Bukkit. Since you're now the one creating the component, this parsing is never done and the client ends up with a different chat component than expected.

// This is what you end up with:
BungeeComponentWrapper wrapper = new BungeeComponentWrapper(new BaseComponent[]{new TranslatableComponent("2")});
BungeeComponentWrapper localized = wrapper.localized("test-lang");
ComponentSerializer.toString(localized); // {"components":[{"extra":[{"text":"§x§4§e§6§6§5§4colored"}],"text":""}]}

// This is what you expect:
BaseComponent[] legacyParsed = TextComponent.fromLegacyText(coloredString2);
ComponentSerializer.toString(legacyParsed); // {"color":"#4e6654","text":"colored"}

The second string is red because the last character of the hex code is 4, which is the legacy color code for dark red.

Instead of using legacy color codes, you should set the color in the chat components themselves:

String coloredString1 = "purple";
String coloredString2 = "colored";
String coloredString3 = "gray";

/* [...] */

Item translateItem = new SimpleItem(new ItemBuilder(Material.BOOK).addLoreLines(
    new ComponentBuilder(new TranslatableComponent("1")).color(ChatColor.DARK_PURPLE).create(),
    new ComponentBuilder(new TranslatableComponent("2")).color(ChatColor.of("#4e6654")).create(),
    new ComponentBuilder(new TranslatableComponent("3")).color(ChatColor.GRAY).create()
));

I'd also recommend you to use adventure text components. This library is built into Paper, but can also be used in Bukkit plugins. Their components are much easier to work with. The only downside is that you'll need to manually wrap those components in AdventureComponentWrapper before passing them to any InvUI method. (This is to retain compatibility with Spigot)

D0gmaDev commented 1 year ago

Hi again @NichtStudioCode Thank you for your explainations, now I pefectly get why it's doing that. However, I'm afraid it remains a problem for me if the inventory's implementation doesn't know in advance what color(s) the translation needs to be.

Currently I have string like that in a file:

casier_reason=<c:#4e6654>Reason: §7%s

and some colorize method before passing it to the language HashMap

public static String colorize(String source) {
    return COLOR_PATTERN.matcher(source).replaceAll(matchResult -> ChatColor.of("#" + matchResult.group(1)).toString());
}

Maybe I can somehow replicate the way sendMessage() converts colors ?

UPDATE: Looking in the TextComponent#fromLegacyText, maybe I just need to replicate that ?

NichtStudioCode commented 1 year ago

I've added ComponentLocalizer#setComponentCreator in v1.1. You should now be able to use Bukkit's legacy format using this:

BungeeComponentLocalizer.getInstance().setComponentCreator((text) -> {
    BaseComponent[] components = TextComponent.fromLegacyText(text);
    TextComponent outer = new TextComponent();
    for (BaseComponent child : components) {
        outer.addExtra(child);
    }
    return outer;
});