From 67939a1dc66ff5145f92b84800a15e3a878be986 Mon Sep 17 00:00:00 2001 From: Wide_Cat Date: Sun, 2 Nov 2025 20:21:35 +0000 Subject: [PATCH 01/21] Add our own custom translation system Reorganised the translation files and keys. Lots of work still to be done to change our hardcoded strings into translatable ones. We should no longer be susceptible to detection by servers using the translation exploit. --- .../mixin/AbstractSignEditScreenMixin.java | 47 -------- .../mixin/ControlListWidgetMixin.java | 25 ++++ .../meteorclient/mixin/DownloaderMixin.java | 7 +- .../mixin/KeyBindingCategoryMixin.java | 30 +++++ .../mixin/LanguageManagerMixin.java | 21 ++++ .../utils/misc/MeteorTranslations.java | 113 ++++++++++++++++++ .../utils/misc/input/KeyBinds.java | 14 ++- .../assets/meteor-client/lang/en_gb.json | 5 - .../assets/meteor-client/lang/en_us.json | 5 - .../assets/meteor-client/lang/hi_in.json | 4 - .../assets/meteor-client/lang/pt_br.json | 4 - .../assets/meteor-client/lang/vi_vn.json | 4 - .../assets/meteor-client/lang/zh_cn.json | 4 - .../assets/meteor-client/language/en_gb.json | 5 + .../assets/meteor-client/language/en_us.json | 5 + .../assets/meteor-client/language/hi_in.json | 4 + .../assets/meteor-client/language/pt_br.json | 4 + .../assets/meteor-client/language/vi_vn.json | 4 + .../assets/meteor-client/language/zh_cn.json | 4 + src/main/resources/meteor-client.mixins.json | 4 +- 20 files changed, 230 insertions(+), 83 deletions(-) delete mode 100644 src/main/java/meteordevelopment/meteorclient/mixin/AbstractSignEditScreenMixin.java create mode 100644 src/main/java/meteordevelopment/meteorclient/mixin/ControlListWidgetMixin.java create mode 100644 src/main/java/meteordevelopment/meteorclient/mixin/KeyBindingCategoryMixin.java create mode 100644 src/main/java/meteordevelopment/meteorclient/mixin/LanguageManagerMixin.java create mode 100644 src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java delete mode 100644 src/main/resources/assets/meteor-client/lang/en_gb.json delete mode 100644 src/main/resources/assets/meteor-client/lang/en_us.json delete mode 100644 src/main/resources/assets/meteor-client/lang/hi_in.json delete mode 100644 src/main/resources/assets/meteor-client/lang/pt_br.json delete mode 100644 src/main/resources/assets/meteor-client/lang/vi_vn.json delete mode 100644 src/main/resources/assets/meteor-client/lang/zh_cn.json create mode 100644 src/main/resources/assets/meteor-client/language/en_gb.json create mode 100644 src/main/resources/assets/meteor-client/language/en_us.json create mode 100644 src/main/resources/assets/meteor-client/language/hi_in.json create mode 100644 src/main/resources/assets/meteor-client/language/pt_br.json create mode 100644 src/main/resources/assets/meteor-client/language/vi_vn.json create mode 100644 src/main/resources/assets/meteor-client/language/zh_cn.json diff --git a/src/main/java/meteordevelopment/meteorclient/mixin/AbstractSignEditScreenMixin.java b/src/main/java/meteordevelopment/meteorclient/mixin/AbstractSignEditScreenMixin.java deleted file mode 100644 index 68cb072b41..0000000000 --- a/src/main/java/meteordevelopment/meteorclient/mixin/AbstractSignEditScreenMixin.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * This file is part of the Meteor Client distribution (https://github.com/MeteorDevelopment/meteor-client). - * Copyright (c) Meteor Development. - */ - -package meteordevelopment.meteorclient.mixin; - -import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import net.minecraft.client.gui.screen.ingame.AbstractSignEditScreen; -import net.minecraft.text.*; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; - -import java.util.stream.Stream; - -@Mixin(AbstractSignEditScreen.class) -public abstract class AbstractSignEditScreenMixin { - @ModifyExpressionValue(method = "(Lnet/minecraft/block/entity/SignBlockEntity;ZZLnet/minecraft/text/Text;)V", at = @At(value = "INVOKE", target = "Ljava/util/stream/IntStream;mapToObj(Ljava/util/function/IntFunction;)Ljava/util/stream/Stream;")) - private Stream modifyTranslatableText(Stream original) { - return original.map(this::modifyText); - } - - // based on https://github.com/JustAlittleWolf/ModDetectionPreventer - @Unique - private Text modifyText(Text message) { - MutableText modified = MutableText.of(message.getContent()); - - if (message.getContent() instanceof KeybindTextContent content) { - String key = content.getKey(); - - if (key.contains("meteor-client")) modified = MutableText.of(new PlainTextContent.Literal(key)); - } - if (message.getContent() instanceof TranslatableTextContent content) { - String key = content.getKey(); - - if (key.contains("meteor-client")) modified = MutableText.of(new PlainTextContent.Literal(key)); - } - - modified.setStyle(message.getStyle()); - for (Text sibling : message.getSiblings()) { - modified.append(modifyText(sibling)); - } - - return modified; - } -} diff --git a/src/main/java/meteordevelopment/meteorclient/mixin/ControlListWidgetMixin.java b/src/main/java/meteordevelopment/meteorclient/mixin/ControlListWidgetMixin.java new file mode 100644 index 0000000000..416e592ad6 --- /dev/null +++ b/src/main/java/meteordevelopment/meteorclient/mixin/ControlListWidgetMixin.java @@ -0,0 +1,25 @@ +/* + * This file is part of the Meteor Client distribution (https://github.com/MeteorDevelopment/meteor-client). + * Copyright (c) Meteor Development. + */ + +package meteordevelopment.meteorclient.mixin; + +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import com.llamalad7.mixinextras.sugar.Local; +import meteordevelopment.meteorclient.utils.misc.MeteorTranslations; +import net.minecraft.client.gui.screen.option.ControlsListWidget; +import net.minecraft.client.option.KeyBinding; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(ControlsListWidget.class) +public class ControlListWidgetMixin { + @ModifyExpressionValue(method = "", at = @At(value = "INVOKE", target = "Lnet/minecraft/text/Text;translatable(Ljava/lang/String;)Lnet/minecraft/text/MutableText;")) + private MutableText modifyText(MutableText original, @Local KeyBinding binding) { + if (binding.getId().startsWith("meteor.key.")) return Text.literal(MeteorTranslations.translate(binding.getId())); + return original; + } +} diff --git a/src/main/java/meteordevelopment/meteorclient/mixin/DownloaderMixin.java b/src/main/java/meteordevelopment/meteorclient/mixin/DownloaderMixin.java index 85362f4815..1da0f0eb09 100644 --- a/src/main/java/meteordevelopment/meteorclient/mixin/DownloaderMixin.java +++ b/src/main/java/meteordevelopment/meteorclient/mixin/DownloaderMixin.java @@ -7,8 +7,8 @@ import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import com.llamalad7.mixinextras.sugar.Local; -import meteordevelopment.meteorclient.MeteorClient; import net.minecraft.util.Downloader; +import net.minecraft.util.Uuids; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -36,10 +36,7 @@ public class DownloaderMixin { @ModifyExpressionValue(method = "method_55485", at = @At(value = "INVOKE", target = "Ljava/nio/file/Path;resolve(Ljava/lang/String;)Ljava/nio/file/Path;")) private Path hookResolve(Path original, @Local(argsOnly = true) UUID id) { UUID accountId = mc.getSession().getUuidOrNull(); - if (accountId == null) { - MeteorClient.LOG.warn("Failed to change resource pack download directory because the account id is null."); - return original; - } + if (accountId == null) accountId = Uuids.getOfflinePlayerUuid(mc.getSession().getUsername()); return directory.resolve(accountId.toString()).resolve(id.toString()); } diff --git a/src/main/java/meteordevelopment/meteorclient/mixin/KeyBindingCategoryMixin.java b/src/main/java/meteordevelopment/meteorclient/mixin/KeyBindingCategoryMixin.java new file mode 100644 index 0000000000..766945a158 --- /dev/null +++ b/src/main/java/meteordevelopment/meteorclient/mixin/KeyBindingCategoryMixin.java @@ -0,0 +1,30 @@ +/* + * This file is part of the Meteor Client distribution (https://github.com/MeteorDevelopment/meteor-client). + * Copyright (c) Meteor Development. + */ + +package meteordevelopment.meteorclient.mixin; + +import com.llamalad7.mixinextras.injector.ModifyReturnValue; +import meteordevelopment.meteorclient.MeteorClient; +import meteordevelopment.meteorclient.utils.misc.MeteorTranslations; +import net.minecraft.client.option.KeyBinding; +import net.minecraft.text.Text; +import net.minecraft.util.Identifier; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(KeyBinding.Category.class) +public class KeyBindingCategoryMixin { + @Shadow + @Final + private Identifier id; + + @ModifyReturnValue(method = "getLabel", at = @At("RETURN")) + private Text modifyLabel(Text original) { + if (id.getNamespace().equals(MeteorClient.MOD_ID)) return Text.literal(MeteorTranslations.translate(id.getPath())); + return original; + } +} diff --git a/src/main/java/meteordevelopment/meteorclient/mixin/LanguageManagerMixin.java b/src/main/java/meteordevelopment/meteorclient/mixin/LanguageManagerMixin.java new file mode 100644 index 0000000000..228143cef8 --- /dev/null +++ b/src/main/java/meteordevelopment/meteorclient/mixin/LanguageManagerMixin.java @@ -0,0 +1,21 @@ +/* + * This file is part of the Meteor Client distribution (https://github.com/MeteorDevelopment/meteor-client). + * Copyright (c) Meteor Development. + */ + +package meteordevelopment.meteorclient.mixin; + +import meteordevelopment.meteorclient.utils.misc.MeteorTranslations; +import net.minecraft.client.resource.language.LanguageManager; +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.CallbackInfo; + +@Mixin(LanguageManager.class) +public class LanguageManagerMixin { + @Inject(method = "setLanguage", at = @At("TAIL")) + private void onSetLanguage(String languageCode, CallbackInfo ci) { + MeteorTranslations.loadLanguage(languageCode); + } +} diff --git a/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java b/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java new file mode 100644 index 0000000000..da77e861aa --- /dev/null +++ b/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java @@ -0,0 +1,113 @@ +/* + * This file is part of the Meteor Client distribution (https://github.com/MeteorDevelopment/meteor-client). + * Copyright (c) Meteor Development. + */ + +package meteordevelopment.meteorclient.utils.misc; + +import com.google.gson.Gson; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import meteordevelopment.meteorclient.MeteorClient; +import meteordevelopment.meteorclient.utils.PreInit; +import net.minecraft.client.resource.language.ReorderingUtil; +import net.minecraft.text.OrderedText; +import net.minecraft.text.StringVisitable; +import net.minecraft.util.Language; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static meteordevelopment.meteorclient.MeteorClient.mc; + +public class MeteorTranslations { + private static final Gson GSON = new Gson(); + private static Map languages; + + @PreInit + public static void init() { + languages = new Object2ObjectOpenHashMap<>(); + List toLoad = new ArrayList<>(2); + toLoad.add("en_us"); + if (!mc.options.language.equalsIgnoreCase("en_us")) toLoad.add(mc.options.language); + + for (String language : toLoad) { + loadLanguage(language); + } + } + + public static void loadLanguage(String language) { + language = language.toLowerCase(); + if (languages.containsKey(language)) return; + + try (InputStream stream = MeteorTranslations.class.getResourceAsStream("/assets/meteor-client/language/" + language + ".json")) { + if (stream == null) { + if (language.equals("en_us")) throw new RuntimeException("Error loading the default language"); + else MeteorClient.LOG.error("Error loading language: {}", language); + return; + } + + // noinspection unchecked + Object2ObjectOpenHashMap map = GSON.fromJson(new InputStreamReader(stream), Object2ObjectOpenHashMap.class); + languages.put(language, new MeteorLanguage(map)); + + MeteorClient.LOG.info("Loaded language: {}", language); + } catch (IOException e) { + if (language.equals("en_us")) throw new RuntimeException(e); + else MeteorClient.LOG.error("Error loading language: {}", language, e); + } + } + + public static String translate(String key) { + if (!key.startsWith("meteor.")) key = "meteor." + key; + return _translate(key); + } + + private static String _translate(String key) { + MeteorLanguage currentLang = getCurrentLanguage(); + return currentLang.hasTranslation(key) ? currentLang.get(key) : getDefaultLanguage().get(key); + } + + public static MeteorLanguage getLanguage(String lang) { + return languages.get(lang); + } + + public static MeteorLanguage getCurrentLanguage() { + return languages.get(mc.options.language.toLowerCase()); + } + + public static MeteorLanguage getDefaultLanguage() { + return languages.get("en_us"); + } + + public static class MeteorLanguage extends Language { + private final Map translations; + + public MeteorLanguage(Map translations) { + this.translations = translations; + } + + @Override + public String get(String key, String fallback) { + return translations.getOrDefault(key, fallback); + } + + @Override + public boolean hasTranslation(String key){ + return translations.containsKey(key); + } + + @Override + public boolean isRightToLeft() { + return false; + } + + @Override + public OrderedText reorder(StringVisitable text) { + return ReorderingUtil.reorder(text, false); + } + } +} diff --git a/src/main/java/meteordevelopment/meteorclient/utils/misc/input/KeyBinds.java b/src/main/java/meteordevelopment/meteorclient/utils/misc/input/KeyBinds.java index 6830326c87..ca346284ad 100644 --- a/src/main/java/meteordevelopment/meteorclient/utils/misc/input/KeyBinds.java +++ b/src/main/java/meteordevelopment/meteorclient/utils/misc/input/KeyBinds.java @@ -11,16 +11,22 @@ import org.lwjgl.glfw.GLFW; public class KeyBinds { - private static final KeyBinding.Category CATEGORY = KeyBinding.Category.create(MeteorClient.identifier("meteor-client")); + /** + * see {@link meteordevelopment.meteorclient.mixin.KeyBindingCategoryMixin} + */ + private static final KeyBinding.Category CATEGORY = KeyBinding.Category.create(MeteorClient.identifier("meteor.key.category")); - public static KeyBinding OPEN_GUI = new KeyBinding("key.meteor-client.open-gui", InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_RIGHT_SHIFT, CATEGORY); - public static KeyBinding OPEN_COMMANDS = new KeyBinding("key.meteor-client.open-commands", InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_PERIOD, CATEGORY); + /** + * see {@link meteordevelopment.meteorclient.mixin.ControlListWidgetMixin} + */ + public static KeyBinding OPEN_GUI = new KeyBinding("meteor.key.open-gui", InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_RIGHT_SHIFT, CATEGORY); + public static KeyBinding OPEN_COMMANDS = new KeyBinding("meteor.key.open-commands", InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_PERIOD, CATEGORY); private KeyBinds() { } public static KeyBinding[] apply(KeyBinding[] binds) { - // Add key binding + // Add key bindings KeyBinding[] newBinds = new KeyBinding[binds.length + 2]; System.arraycopy(binds, 0, newBinds, 0, binds.length); diff --git a/src/main/resources/assets/meteor-client/lang/en_gb.json b/src/main/resources/assets/meteor-client/lang/en_gb.json deleted file mode 100644 index 7b02207810..0000000000 --- a/src/main/resources/assets/meteor-client/lang/en_gb.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "key.meteor-client.open-gui": "Open GUI", - "key.meteor-client.open-commands": "Open Commands", - "key.category.meteor-client.meteor-client": "Meteor Client" -} diff --git a/src/main/resources/assets/meteor-client/lang/en_us.json b/src/main/resources/assets/meteor-client/lang/en_us.json deleted file mode 100644 index 7b02207810..0000000000 --- a/src/main/resources/assets/meteor-client/lang/en_us.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "key.meteor-client.open-gui": "Open GUI", - "key.meteor-client.open-commands": "Open Commands", - "key.category.meteor-client.meteor-client": "Meteor Client" -} diff --git a/src/main/resources/assets/meteor-client/lang/hi_in.json b/src/main/resources/assets/meteor-client/lang/hi_in.json deleted file mode 100644 index 991dfbf4ee..0000000000 --- a/src/main/resources/assets/meteor-client/lang/hi_in.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "key.meteor-client.open-gui": "GUI खोलें", - "key.meteor-client.open-commands": "कमांड खोलें" -} diff --git a/src/main/resources/assets/meteor-client/lang/pt_br.json b/src/main/resources/assets/meteor-client/lang/pt_br.json deleted file mode 100644 index 0056869be4..0000000000 --- a/src/main/resources/assets/meteor-client/lang/pt_br.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "key.meteor-client.open-gui": "Abrir Menu", - "key.meteor-client.open-commands": "Abrir Comandos" -} diff --git a/src/main/resources/assets/meteor-client/lang/vi_vn.json b/src/main/resources/assets/meteor-client/lang/vi_vn.json deleted file mode 100644 index 86372f9041..0000000000 --- a/src/main/resources/assets/meteor-client/lang/vi_vn.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "key.meteor-client.open-gui": "Mở giao diện", - "key.meteor-client.open-commands": "Mở lệnh" -} diff --git a/src/main/resources/assets/meteor-client/lang/zh_cn.json b/src/main/resources/assets/meteor-client/lang/zh_cn.json deleted file mode 100644 index 4c1eaa1207..0000000000 --- a/src/main/resources/assets/meteor-client/lang/zh_cn.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "key.meteor-client.open-gui": "打开GUI", - "key.meteor-client.open-commands": "输入命令" -} diff --git a/src/main/resources/assets/meteor-client/language/en_gb.json b/src/main/resources/assets/meteor-client/language/en_gb.json new file mode 100644 index 0000000000..7dc5eb6bfc --- /dev/null +++ b/src/main/resources/assets/meteor-client/language/en_gb.json @@ -0,0 +1,5 @@ +{ + "meteor.key.category": "Meteor Client", + "meteor.key.open-gui": "Open GUI", + "meteor.key.open-commands": "Open Commands" +} diff --git a/src/main/resources/assets/meteor-client/language/en_us.json b/src/main/resources/assets/meteor-client/language/en_us.json new file mode 100644 index 0000000000..7dc5eb6bfc --- /dev/null +++ b/src/main/resources/assets/meteor-client/language/en_us.json @@ -0,0 +1,5 @@ +{ + "meteor.key.category": "Meteor Client", + "meteor.key.open-gui": "Open GUI", + "meteor.key.open-commands": "Open Commands" +} diff --git a/src/main/resources/assets/meteor-client/language/hi_in.json b/src/main/resources/assets/meteor-client/language/hi_in.json new file mode 100644 index 0000000000..563d10c7b6 --- /dev/null +++ b/src/main/resources/assets/meteor-client/language/hi_in.json @@ -0,0 +1,4 @@ +{ + "meteor.key.open-gui": "GUI खोलें", + "meteor.key.open-commands": "कमांड खोलें" +} diff --git a/src/main/resources/assets/meteor-client/language/pt_br.json b/src/main/resources/assets/meteor-client/language/pt_br.json new file mode 100644 index 0000000000..8a593c9df3 --- /dev/null +++ b/src/main/resources/assets/meteor-client/language/pt_br.json @@ -0,0 +1,4 @@ +{ + "meteor.key.open-gui": "Abrir Menu", + "meteor.key.open-commands": "Abrir Comandos" +} diff --git a/src/main/resources/assets/meteor-client/language/vi_vn.json b/src/main/resources/assets/meteor-client/language/vi_vn.json new file mode 100644 index 0000000000..eec2d74d6c --- /dev/null +++ b/src/main/resources/assets/meteor-client/language/vi_vn.json @@ -0,0 +1,4 @@ +{ + "meteor.key.open-gui": "Mở giao diện", + "meteor.key.open-commands": "Mở lệnh" +} diff --git a/src/main/resources/assets/meteor-client/language/zh_cn.json b/src/main/resources/assets/meteor-client/language/zh_cn.json new file mode 100644 index 0000000000..6ca1f878e0 --- /dev/null +++ b/src/main/resources/assets/meteor-client/language/zh_cn.json @@ -0,0 +1,4 @@ +{ + "meteor.key.open-gui": "打开GUI", + "meteor.key.open-commands": "输入命令" +} diff --git a/src/main/resources/meteor-client.mixins.json b/src/main/resources/meteor-client.mixins.json index 039572d42c..991ca13042 100644 --- a/src/main/resources/meteor-client.mixins.json +++ b/src/main/resources/meteor-client.mixins.json @@ -14,7 +14,6 @@ "AbstractFurnaceScreenMixin", "AbstractSignBlockEntityRendererMixin", "AbstractSignEditScreenAccessor", - "AbstractSignEditScreenMixin", "ArmorFeatureRendererMixin", "BakedQuadMixin", "BannerBlockEntityRendererMixin", @@ -67,6 +66,7 @@ "ConnectScreenMixin", "ContainerComponentAccessor", "ContainerComponentMixin", + "ControlListWidgetMixin", "CrashReportMixin", "CreativeInventoryScreenAccessor", "CreativeSlotMixin", @@ -116,8 +116,10 @@ "ItemRenderStateAccessor", "ItemStackMixin", "KeyBindingAccessor", + "KeyBindingCategoryMixin", "KeyboardInputMixin", "KeyboardMixin", + "LanguageManagerMixin", "LayerRenderStateAccessor", "LightmapTextureManagerMixin", "LightningEntityRendererMixin", From 49a2a44f3233e65d23f556454d8e13467747bc87 Mon Sep 17 00:00:00 2001 From: Wide_Cat Date: Mon, 3 Nov 2025 01:35:09 +0000 Subject: [PATCH 02/21] Add a method for addons to provide their own language files to be used by the translation system --- .../meteorclient/addons/MeteorAddon.java | 11 +++ .../utils/misc/MeteorTranslations.java | 83 ++++++++++++++----- 2 files changed, 72 insertions(+), 22 deletions(-) diff --git a/src/main/java/meteordevelopment/meteorclient/addons/MeteorAddon.java b/src/main/java/meteordevelopment/meteorclient/addons/MeteorAddon.java index 8499e60521..c89f917b67 100644 --- a/src/main/java/meteordevelopment/meteorclient/addons/MeteorAddon.java +++ b/src/main/java/meteordevelopment/meteorclient/addons/MeteorAddon.java @@ -7,6 +7,8 @@ import meteordevelopment.meteorclient.utils.render.color.Color; +import java.io.InputStream; + public abstract class MeteorAddon { /** This field is automatically assigned from fabric.mod.json file. */ public String name; @@ -34,4 +36,13 @@ public GithubRepo getRepo() { public String getCommit() { return null; } + + /** + * @param lang A language code in lowercase + * @return An InputStream for the relevant json translation file, or null if the addon doesn't have + * a file for that language. + */ + public InputStream provideLanguage(String lang) { + return null; + } } diff --git a/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java b/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java index da77e861aa..f75cb38a19 100644 --- a/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java +++ b/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java @@ -7,7 +7,10 @@ import com.google.gson.Gson; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; import meteordevelopment.meteorclient.MeteorClient; +import meteordevelopment.meteorclient.addons.AddonManager; +import meteordevelopment.meteorclient.addons.MeteorAddon; import meteordevelopment.meteorclient.utils.PreInit; import net.minecraft.client.resource.language.ReorderingUtil; import net.minecraft.text.OrderedText; @@ -25,11 +28,10 @@ public class MeteorTranslations { private static final Gson GSON = new Gson(); - private static Map languages; + private static final Map languages = new Object2ObjectOpenHashMap<>(); @PreInit - public static void init() { - languages = new Object2ObjectOpenHashMap<>(); + public static void preInit() { List toLoad = new ArrayList<>(2); toLoad.add("en_us"); if (!mc.options.language.equalsIgnoreCase("en_us")) toLoad.add(mc.options.language); @@ -39,34 +41,47 @@ public static void init() { } } - public static void loadLanguage(String language) { - language = language.toLowerCase(); - if (languages.containsKey(language)) return; + public static void loadLanguage(String languageCode) { + languageCode = languageCode.toLowerCase(); + if (languages.containsKey(languageCode)) return; - try (InputStream stream = MeteorTranslations.class.getResourceAsStream("/assets/meteor-client/language/" + language + ".json")) { + try (InputStream stream = MeteorTranslations.class.getResourceAsStream("/assets/meteor-client/language/" + languageCode + ".json")) { if (stream == null) { - if (language.equals("en_us")) throw new RuntimeException("Error loading the default language"); - else MeteorClient.LOG.error("Error loading language: {}", language); + if (languageCode.equals("en_us")) throw new RuntimeException("Error loading the default language"); + else MeteorClient.LOG.info("No language file found for '{}'", languageCode); return; } // noinspection unchecked Object2ObjectOpenHashMap map = GSON.fromJson(new InputStreamReader(stream), Object2ObjectOpenHashMap.class); - languages.put(language, new MeteorLanguage(map)); + languages.put(languageCode, new MeteorLanguage(map)); - MeteorClient.LOG.info("Loaded language: {}", language); + MeteorClient.LOG.info("Loaded language: {}", languageCode); } catch (IOException e) { - if (language.equals("en_us")) throw new RuntimeException(e); - else MeteorClient.LOG.error("Error loading language: {}", language, e); + if (languageCode.equals("en_us")) throw new RuntimeException(e); + else MeteorClient.LOG.error("Error loading language: {}", languageCode, e); } - } - public static String translate(String key) { - if (!key.startsWith("meteor.")) key = "meteor." + key; - return _translate(key); + for (MeteorAddon addon : AddonManager.ADDONS) { + if (addon == MeteorClient.ADDON) continue; + + try (InputStream stream = addon.provideLanguage(languageCode)) { + if (stream == null) continue; + MeteorLanguage lang = languages.getOrDefault(languageCode, new MeteorLanguage()); + + // noinspection unchecked + Object2ObjectOpenHashMap map = GSON.fromJson(new InputStreamReader(stream), Object2ObjectOpenHashMap.class); + lang.addCustomTranslation(map); + languages.put(languageCode, lang); + + MeteorClient.LOG.info("Loaded language {} from addon {}", languageCode, addon.name); + } catch (IOException e) { + MeteorClient.LOG.error("Error loading language {} from addon {}", languageCode, addon.name, e); + } + } } - private static String _translate(String key) { + public static String translate(String key) { MeteorLanguage currentLang = getCurrentLanguage(); return currentLang.hasTranslation(key) ? currentLang.get(key) : getDefaultLanguage().get(key); } @@ -76,7 +91,7 @@ public static MeteorLanguage getLanguage(String lang) { } public static MeteorLanguage getCurrentLanguage() { - return languages.get(mc.options.language.toLowerCase()); + return languages.getOrDefault(mc.options.language.toLowerCase(), getDefaultLanguage()); } public static MeteorLanguage getDefaultLanguage() { @@ -85,19 +100,43 @@ public static MeteorLanguage getDefaultLanguage() { public static class MeteorLanguage extends Language { private final Map translations; + private final List> customTranslations = new ObjectArrayList<>(); + + public MeteorLanguage() { + this.translations = new Object2ObjectOpenHashMap<>(); + } public MeteorLanguage(Map translations) { this.translations = translations; } + @SuppressWarnings("unused") + public void addCustomTranslation(Map customTranslation) { + if (customTranslations.contains(customTranslation)) return; + + customTranslations.add(customTranslation); + } + @Override public String get(String key, String fallback) { - return translations.getOrDefault(key, fallback); + if (translations.containsKey(key)) return translations.get(key); + + for (Map customTranslation : customTranslations) { + if (customTranslation.containsKey(key)) return customTranslation.get(key); + } + + return fallback; } @Override - public boolean hasTranslation(String key){ - return translations.containsKey(key); + public boolean hasTranslation(String key) { + if (translations.containsKey(key)) return true; + + for (Map customTranslation : customTranslations) { + if (customTranslation.containsKey(key)) return true; + } + + return false; } @Override From e5afd008af7a401e52a5d6e70221fa7bccf550c3 Mon Sep 17 00:00:00 2001 From: Wide_Cat Date: Mon, 3 Nov 2025 01:59:20 +0000 Subject: [PATCH 03/21] Fix loading addon translations and add more documentation --- .../meteorclient/addons/MeteorAddon.java | 13 +++++++++++++ .../utils/misc/MeteorTranslations.java | 14 +++++++------- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/main/java/meteordevelopment/meteorclient/addons/MeteorAddon.java b/src/main/java/meteordevelopment/meteorclient/addons/MeteorAddon.java index c89f917b67..7ae5918d6f 100644 --- a/src/main/java/meteordevelopment/meteorclient/addons/MeteorAddon.java +++ b/src/main/java/meteordevelopment/meteorclient/addons/MeteorAddon.java @@ -38,6 +38,19 @@ public String getCommit() { } /** + * Example implementation: + *
{@code
+     *  @Override
+     *  public InputStream provideLanguage(String lang) {
+     *      return Addon.class.getResourceAsStream("/assets/addon-name/language/" + lang + ".json")
+     *  }
+     * }
+     * 

+ * + * Addons should not store their language files in the /assets/xxx/lang/ path as it opens up users to detection + * by servers via the translation exploit. + * Storing them anywhere else should prevent them from getting picked up via the vanilla resource loader. + * * @param lang A language code in lowercase * @return An InputStream for the relevant json translation file, or null if the addon doesn't have * a file for that language. diff --git a/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java b/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java index f75cb38a19..c2142b5823 100644 --- a/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java +++ b/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java @@ -26,6 +26,7 @@ import static meteordevelopment.meteorclient.MeteorClient.mc; +@SuppressWarnings("unused") public class MeteorTranslations { private static final Gson GSON = new Gson(); private static final Map languages = new Object2ObjectOpenHashMap<>(); @@ -49,14 +50,14 @@ public static void loadLanguage(String languageCode) { if (stream == null) { if (languageCode.equals("en_us")) throw new RuntimeException("Error loading the default language"); else MeteorClient.LOG.info("No language file found for '{}'", languageCode); - return; } + else { + // noinspection unchecked + Object2ObjectOpenHashMap map = GSON.fromJson(new InputStreamReader(stream), Object2ObjectOpenHashMap.class); + languages.put(languageCode, new MeteorLanguage(map)); - // noinspection unchecked - Object2ObjectOpenHashMap map = GSON.fromJson(new InputStreamReader(stream), Object2ObjectOpenHashMap.class); - languages.put(languageCode, new MeteorLanguage(map)); - - MeteorClient.LOG.info("Loaded language: {}", languageCode); + MeteorClient.LOG.info("Loaded language: {}", languageCode); + } } catch (IOException e) { if (languageCode.equals("en_us")) throw new RuntimeException(e); else MeteorClient.LOG.error("Error loading language: {}", languageCode, e); @@ -110,7 +111,6 @@ public MeteorLanguage(Map translations) { this.translations = translations; } - @SuppressWarnings("unused") public void addCustomTranslation(Map customTranslation) { if (customTranslations.contains(customTranslation)) return; From f4035ea5962bae1efd40d3586af8920058865577 Mon Sep 17 00:00:00 2001 From: Wide_Cat Date: Mon, 3 Nov 2025 14:30:22 +0000 Subject: [PATCH 04/21] Extract command descriptions into translatable components --- .../meteorclient/commands/Command.java | 14 ++++++- .../commands/commands/BindCommand.java | 2 +- .../commands/commands/BindsCommand.java | 2 +- .../commands/commands/DismountCommand.java | 2 +- .../commands/commands/DropCommand.java | 2 +- .../commands/commands/EnchantCommand.java | 2 +- .../commands/commands/FakePlayerCommand.java | 2 +- .../commands/commands/FovCommand.java | 2 +- .../commands/commands/FriendsCommand.java | 2 +- .../commands/commands/GiveCommand.java | 2 +- .../commands/commands/HClipCommand.java | 2 +- .../commands/commands/InputCommand.java | 2 +- .../commands/commands/MacroCommand.java | 2 +- .../commands/commands/NbtCommand.java | 2 +- .../commands/commands/NotebotCommand.java | 2 +- .../commands/commands/PeekCommand.java | 2 +- .../commands/commands/ProfilesCommand.java | 2 +- .../commands/commands/ReloadCommand.java | 2 +- .../commands/commands/ResetCommand.java | 2 +- .../commands/commands/RotationCommand.java | 2 +- .../commands/commands/SayCommand.java | 2 +- .../commands/commands/ServerCommand.java | 2 +- .../commands/commands/SpectateCommand.java | 2 +- .../commands/commands/SwarmCommand.java | 2 +- .../commands/commands/VClipCommand.java | 2 +- .../commands/commands/WaspCommand.java | 2 +- .../utils/misc/MeteorTranslations.java | 7 +++- .../assets/meteor-client/language/en_gb.json | 41 ++++++++++++++++++- .../assets/meteor-client/language/en_us.json | 41 ++++++++++++++++++- 29 files changed, 123 insertions(+), 30 deletions(-) diff --git a/src/main/java/meteordevelopment/meteorclient/commands/Command.java b/src/main/java/meteordevelopment/meteorclient/commands/Command.java index 18fea76782..c61ffc4357 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/Command.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/Command.java @@ -12,6 +12,7 @@ import meteordevelopment.meteorclient.MeteorClient; import meteordevelopment.meteorclient.systems.config.Config; import meteordevelopment.meteorclient.utils.Utils; +import meteordevelopment.meteorclient.utils.misc.MeteorTranslations; import meteordevelopment.meteorclient.utils.player.ChatUtils; import net.minecraft.client.MinecraftClient; import net.minecraft.command.CommandRegistryAccess; @@ -32,13 +33,22 @@ public abstract class Command { private final String description; private final List aliases; + // todo remove in the next minecraft version update + @Deprecated(forRemoval = true) public Command(String name, String description, String... aliases) { this.name = name; this.title = Utils.nameToTitle(name); - this.description = description; + this.description = "meteor.command." + name + ".description"; this.aliases = List.of(aliases); } + public Command(String name) { + this.name = name; + this.title = Utils.nameToTitle(name); + this.description = "meteor.command." + name + ".description"; + this.aliases = List.of(); + } + // Helper methods to painlessly infer the CommandSource generic type argument protected static RequiredArgumentBuilder argument(final String name, final ArgumentType type) { return RequiredArgumentBuilder.argument(name, type); @@ -66,7 +76,7 @@ public String getName() { } public String getDescription() { - return description; + return MeteorTranslations.translate(description); } public List getAliases() { diff --git a/src/main/java/meteordevelopment/meteorclient/commands/commands/BindCommand.java b/src/main/java/meteordevelopment/meteorclient/commands/commands/BindCommand.java index 3ef50aa80d..98302c02fd 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/commands/BindCommand.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/commands/BindCommand.java @@ -14,7 +14,7 @@ public class BindCommand extends Command { public BindCommand() { - super("bind", "Binds a specified module to the next pressed key."); + super("bind"); } @Override diff --git a/src/main/java/meteordevelopment/meteorclient/commands/commands/BindsCommand.java b/src/main/java/meteordevelopment/meteorclient/commands/commands/BindsCommand.java index 0bc90a5ea4..d21e6352d7 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/commands/BindsCommand.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/commands/BindsCommand.java @@ -21,7 +21,7 @@ public class BindsCommand extends Command { public BindsCommand() { - super("binds", "List of all bound modules."); + super("binds"); } @Override diff --git a/src/main/java/meteordevelopment/meteorclient/commands/commands/DismountCommand.java b/src/main/java/meteordevelopment/meteorclient/commands/commands/DismountCommand.java index 1cbdfb84cd..78253c2e8c 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/commands/DismountCommand.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/commands/DismountCommand.java @@ -13,7 +13,7 @@ public class DismountCommand extends Command { public DismountCommand() { - super("dismount", "Dismounts you from entity you are riding."); + super("dismount"); } @Override diff --git a/src/main/java/meteordevelopment/meteorclient/commands/commands/DropCommand.java b/src/main/java/meteordevelopment/meteorclient/commands/commands/DropCommand.java index 4816bedd09..e6d7c202c4 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/commands/DropCommand.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/commands/DropCommand.java @@ -26,7 +26,7 @@ public class DropCommand extends Command { private static final SimpleCommandExceptionType NO_SUCH_ITEM = new SimpleCommandExceptionType(Text.literal("Could not find an item with that name!")); public DropCommand() { - super("drop", "Automatically drops specified items."); + super("drop"); } @Override diff --git a/src/main/java/meteordevelopment/meteorclient/commands/commands/EnchantCommand.java b/src/main/java/meteordevelopment/meteorclient/commands/commands/EnchantCommand.java index 7eb9e6b584..6d4f03279a 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/commands/EnchantCommand.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/commands/EnchantCommand.java @@ -28,7 +28,7 @@ public class EnchantCommand extends Command { private static final SimpleCommandExceptionType NOT_HOLDING_ITEM = new SimpleCommandExceptionType(Text.literal("You need to hold some item to enchant.")); public EnchantCommand() { - super("enchant", "Enchants the item in your hand. REQUIRES Creative mode."); + super("enchant"); } @Override diff --git a/src/main/java/meteordevelopment/meteorclient/commands/commands/FakePlayerCommand.java b/src/main/java/meteordevelopment/meteorclient/commands/commands/FakePlayerCommand.java index 5374e1f503..8806330029 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/commands/FakePlayerCommand.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/commands/FakePlayerCommand.java @@ -18,7 +18,7 @@ public class FakePlayerCommand extends Command { public FakePlayerCommand() { - super("fake-player", "Manages fake players that you can use for testing."); + super("fake-player"); } @Override diff --git a/src/main/java/meteordevelopment/meteorclient/commands/commands/FovCommand.java b/src/main/java/meteordevelopment/meteorclient/commands/commands/FovCommand.java index dd612d030a..bf5aef1948 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/commands/FovCommand.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/commands/FovCommand.java @@ -13,7 +13,7 @@ public class FovCommand extends Command { public FovCommand() { - super("fov", "Changes your fov."); + super("fov"); } @Override diff --git a/src/main/java/meteordevelopment/meteorclient/commands/commands/FriendsCommand.java b/src/main/java/meteordevelopment/meteorclient/commands/commands/FriendsCommand.java index 23ae209cad..84d12f56c6 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/commands/FriendsCommand.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/commands/FriendsCommand.java @@ -18,7 +18,7 @@ public class FriendsCommand extends Command { public FriendsCommand() { - super("friends", "Manages friends."); + super("friends"); } @Override diff --git a/src/main/java/meteordevelopment/meteorclient/commands/commands/GiveCommand.java b/src/main/java/meteordevelopment/meteorclient/commands/commands/GiveCommand.java index 94150a58b7..fa802ede81 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/commands/GiveCommand.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/commands/GiveCommand.java @@ -23,7 +23,7 @@ public class GiveCommand extends Command { private final static SimpleCommandExceptionType NO_SPACE = new SimpleCommandExceptionType(Text.literal("No space in hotbar.")); public GiveCommand() { - super("give", "Gives you any item."); + super("give"); } @Override diff --git a/src/main/java/meteordevelopment/meteorclient/commands/commands/HClipCommand.java b/src/main/java/meteordevelopment/meteorclient/commands/commands/HClipCommand.java index 2af1daa8f3..11528ac7cf 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/commands/HClipCommand.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/commands/HClipCommand.java @@ -14,7 +14,7 @@ public class HClipCommand extends Command { public HClipCommand() { - super("hclip", "Lets you clip through blocks horizontally."); + super("hclip"); } @Override diff --git a/src/main/java/meteordevelopment/meteorclient/commands/commands/InputCommand.java b/src/main/java/meteordevelopment/meteorclient/commands/commands/InputCommand.java index e453767910..c16e3745da 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/commands/InputCommand.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/commands/InputCommand.java @@ -41,7 +41,7 @@ public class InputCommand extends Command { ); public InputCommand() { - super("input", "Keyboard input simulation."); + super("input"); } @Override diff --git a/src/main/java/meteordevelopment/meteorclient/commands/commands/MacroCommand.java b/src/main/java/meteordevelopment/meteorclient/commands/commands/MacroCommand.java index 074c9eae8d..22c899af11 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/commands/MacroCommand.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/commands/MacroCommand.java @@ -21,7 +21,7 @@ public class MacroCommand extends Command { public MacroCommand() { - super("macro", "Allows you to execute macros."); + super("macro"); MeteorClient.EVENT_BUS.subscribe(this); } diff --git a/src/main/java/meteordevelopment/meteorclient/commands/commands/NbtCommand.java b/src/main/java/meteordevelopment/meteorclient/commands/commands/NbtCommand.java index 1eea843b3a..667b28c421 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/commands/NbtCommand.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/commands/NbtCommand.java @@ -51,7 +51,7 @@ public class NbtCommand extends Command { ))); public NbtCommand() { - super("nbt", "Modifies NBT data for an item, example: .nbt add {display:{Name:'{\"text\":\"$cRed Name\"}'}}"); + super("nbt"); } @Override diff --git a/src/main/java/meteordevelopment/meteorclient/commands/commands/NotebotCommand.java b/src/main/java/meteordevelopment/meteorclient/commands/commands/NotebotCommand.java index ee791ce2fc..cf6822fb2e 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/commands/NotebotCommand.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/commands/NotebotCommand.java @@ -41,7 +41,7 @@ public class NotebotCommand extends Command { private final Map> song = new HashMap<>(); // tick -> notes public NotebotCommand() { - super("notebot", "Allows you load notebot files"); + super("notebot"); } @Override diff --git a/src/main/java/meteordevelopment/meteorclient/commands/commands/PeekCommand.java b/src/main/java/meteordevelopment/meteorclient/commands/commands/PeekCommand.java index 52e9761622..77d2ac6dcf 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/commands/PeekCommand.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/commands/PeekCommand.java @@ -19,7 +19,7 @@ public class PeekCommand extends Command { private static final SimpleCommandExceptionType CANT_PEEK = new SimpleCommandExceptionType(Text.literal("You must be holding a storage block or looking at an item frame.")); public PeekCommand() { - super("peek", "Lets you see what's inside storage block items."); + super("peek"); } @Override diff --git a/src/main/java/meteordevelopment/meteorclient/commands/commands/ProfilesCommand.java b/src/main/java/meteordevelopment/meteorclient/commands/commands/ProfilesCommand.java index 43c816f1c1..fa5ccf9412 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/commands/ProfilesCommand.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/commands/ProfilesCommand.java @@ -15,7 +15,7 @@ public class ProfilesCommand extends Command { public ProfilesCommand() { - super("profiles", "Loads and saves profiles."); + super("profiles"); } @Override diff --git a/src/main/java/meteordevelopment/meteorclient/commands/commands/ReloadCommand.java b/src/main/java/meteordevelopment/meteorclient/commands/commands/ReloadCommand.java index f299819854..3e7304c5a8 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/commands/ReloadCommand.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/commands/ReloadCommand.java @@ -17,7 +17,7 @@ public class ReloadCommand extends Command { public ReloadCommand() { - super("reload", "Reloads many systems."); + super("reload"); } @Override diff --git a/src/main/java/meteordevelopment/meteorclient/commands/commands/ResetCommand.java b/src/main/java/meteordevelopment/meteorclient/commands/commands/ResetCommand.java index 3377b7d7e1..d5cd90994e 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/commands/ResetCommand.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/commands/ResetCommand.java @@ -19,7 +19,7 @@ public class ResetCommand extends Command { public ResetCommand() { - super("reset", "Resets specified settings."); + super("reset"); } @Override diff --git a/src/main/java/meteordevelopment/meteorclient/commands/commands/RotationCommand.java b/src/main/java/meteordevelopment/meteorclient/commands/commands/RotationCommand.java index d9b3be2622..ec0aa00d1b 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/commands/RotationCommand.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/commands/RotationCommand.java @@ -15,7 +15,7 @@ public class RotationCommand extends Command { public RotationCommand() { - super("rotation", "Modifies your rotation."); + super("rotation"); } @Override diff --git a/src/main/java/meteordevelopment/meteorclient/commands/commands/SayCommand.java b/src/main/java/meteordevelopment/meteorclient/commands/commands/SayCommand.java index 102bc003fe..298a3512c0 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/commands/SayCommand.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/commands/SayCommand.java @@ -23,7 +23,7 @@ public class SayCommand extends Command { public SayCommand() { - super("say", "Sends messages in chat."); + super("say"); } @Override diff --git a/src/main/java/meteordevelopment/meteorclient/commands/commands/ServerCommand.java b/src/main/java/meteordevelopment/meteorclient/commands/commands/ServerCommand.java index c8441e85c0..969cdaa8b8 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/commands/ServerCommand.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/commands/ServerCommand.java @@ -50,7 +50,7 @@ public class ServerCommand extends Command { public ServerCommand() { - super("server", "Prints server information"); + super("server"); MeteorClient.EVENT_BUS.subscribe(this); } diff --git a/src/main/java/meteordevelopment/meteorclient/commands/commands/SpectateCommand.java b/src/main/java/meteordevelopment/meteorclient/commands/commands/SpectateCommand.java index d54ed7cce9..deefaa9f60 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/commands/SpectateCommand.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/commands/SpectateCommand.java @@ -21,7 +21,7 @@ public class SpectateCommand extends Command { private final StaticListener shiftListener = new StaticListener(); public SpectateCommand() { - super("spectate", "Allows you to spectate nearby players"); + super("spectate"); } @Override diff --git a/src/main/java/meteordevelopment/meteorclient/commands/commands/SwarmCommand.java b/src/main/java/meteordevelopment/meteorclient/commands/commands/SwarmCommand.java index 555b73793d..1b39b2b3d9 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/commands/SwarmCommand.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/commands/SwarmCommand.java @@ -43,7 +43,7 @@ public class SwarmCommand extends Command { private @Nullable ObjectIntPair pendingConnection; public SwarmCommand() { - super("swarm", "Sends commands to connected swarm workers."); + super("swarm"); } @Override diff --git a/src/main/java/meteordevelopment/meteorclient/commands/commands/VClipCommand.java b/src/main/java/meteordevelopment/meteorclient/commands/commands/VClipCommand.java index 13124e5a54..d1ee18751c 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/commands/VClipCommand.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/commands/VClipCommand.java @@ -14,7 +14,7 @@ public class VClipCommand extends Command { public VClipCommand() { - super("vclip", "Lets you clip through blocks vertically."); + super("vclip"); } @Override diff --git a/src/main/java/meteordevelopment/meteorclient/commands/commands/WaspCommand.java b/src/main/java/meteordevelopment/meteorclient/commands/commands/WaspCommand.java index 1b3fb81e6a..d5d9fb15eb 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/commands/WaspCommand.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/commands/WaspCommand.java @@ -19,7 +19,7 @@ public class WaspCommand extends Command { private static final SimpleCommandExceptionType CANT_WASP_SELF = new SimpleCommandExceptionType(Text.literal("You cannot target yourself!")); public WaspCommand() { - super("wasp", "Sets the auto wasp target."); + super("wasp"); } @Override diff --git a/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java b/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java index c2142b5823..9558feba87 100644 --- a/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java +++ b/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java @@ -84,7 +84,12 @@ public static void loadLanguage(String languageCode) { public static String translate(String key) { MeteorLanguage currentLang = getCurrentLanguage(); - return currentLang.hasTranslation(key) ? currentLang.get(key) : getDefaultLanguage().get(key); + return currentLang.get(key, getDefaultLanguage().get(key)); + } + + public static String translate(String key, String fallback) { + MeteorLanguage currentLang = getCurrentLanguage(); + return currentLang.get(key, getDefaultLanguage().get(key, fallback)); } public static MeteorLanguage getLanguage(String lang) { diff --git a/src/main/resources/assets/meteor-client/language/en_gb.json b/src/main/resources/assets/meteor-client/language/en_gb.json index 7dc5eb6bfc..03d3bb501e 100644 --- a/src/main/resources/assets/meteor-client/language/en_gb.json +++ b/src/main/resources/assets/meteor-client/language/en_gb.json @@ -1,5 +1,44 @@ { "meteor.key.category": "Meteor Client", "meteor.key.open-gui": "Open GUI", - "meteor.key.open-commands": "Open Commands" + "meteor.key.open-commands": "Open Commands", + + "meteor.command.bind.description": "Binds a specified module to the next pressed key.", + "meteor.command.binds.description": "List of all bound modules.", + "meteor.command.commands.description": "List of all commands.", + "meteor.command.damage.description": "Damages self.", + "meteor.command.disconnect.description": "Disconnect from the server", + "meteor.command.dismount.description": "Dismounts you from entity you are riding.", + "meteor.command.drop.description": "Automatically drops specified items.", + "meteor.command.enchant.description": "Enchants the item in your hand. REQUIRES Creative mode.", + "meteor.command.ender-chest.description": "Allows you to preview memory of your ender chest.", + "meteor.command.fake-player.description": "Manages fake players that you can use for testing.", + "meteor.command.fov.description": "Changes your fov.", + "meteor.command.friends.description": "Manages friends.", + "meteor.command.gamemode.description": "Changes your gamemode client-side.", + "meteor.command.give.description": "Gives you any item.", + "meteor.command.hclip.description": "Lets you clip through blocks horizontally.", + "meteor.command.input.description": "Keyboard input simulation.", + "meteor.command.inventory.description": "Allows you to see parts of another player's inventory.", + "meteor.command.locate.description": "Locates structures", + "meteor.command.macro.description": "Allows you to execute macros.", + "meteor.command.modules.description": "Displays a list of all modules.", + "meteor.command.name-history.description": "Provides a list of a players previous names from the laby.net api", + "meteor.command.nbt.description": "Modifies NBT data for an item, example: .nbt add {display:{Name:'{\"text\":\"$cRed Name\"}'}}", + "meteor.command.notebot.description": "Allows you load notebot files", + "meteor.command.peek.description": "Lets you see what's inside storage block items.", + "meteor.command.profiles.description": "Loads and saves profiles.", + "meteor.command.reload.description": "Reloads many systems.", + "meteor.command.reset.description": "Resets specified settings.", + "meteor.command.rotation.description": "Modifies your rotation.", + "meteor.command.save-map.description": "Saves a map to an image.", + "meteor.command.say.description": "Sends messages in chat.", + "meteor.command.server.description": "Prints server information", + "meteor.command.hud.description": "Allows you to view and change module settings.", + "meteor.command.spectate.description": "Allows you to spectate nearby players", + "meteor.command.swarm.description": "Sends commands to connected swarm workers.", + "meteor.command.toggle.description": "Toggles a module.", + "meteor.command.vclip.description": "Lets you clip through blocks vertically.", + "meteor.command.wasp.description": "Sets the auto wasp target.", + "meteor.command.waypoint.description": "Manages waypoints." } diff --git a/src/main/resources/assets/meteor-client/language/en_us.json b/src/main/resources/assets/meteor-client/language/en_us.json index 7dc5eb6bfc..03d3bb501e 100644 --- a/src/main/resources/assets/meteor-client/language/en_us.json +++ b/src/main/resources/assets/meteor-client/language/en_us.json @@ -1,5 +1,44 @@ { "meteor.key.category": "Meteor Client", "meteor.key.open-gui": "Open GUI", - "meteor.key.open-commands": "Open Commands" + "meteor.key.open-commands": "Open Commands", + + "meteor.command.bind.description": "Binds a specified module to the next pressed key.", + "meteor.command.binds.description": "List of all bound modules.", + "meteor.command.commands.description": "List of all commands.", + "meteor.command.damage.description": "Damages self.", + "meteor.command.disconnect.description": "Disconnect from the server", + "meteor.command.dismount.description": "Dismounts you from entity you are riding.", + "meteor.command.drop.description": "Automatically drops specified items.", + "meteor.command.enchant.description": "Enchants the item in your hand. REQUIRES Creative mode.", + "meteor.command.ender-chest.description": "Allows you to preview memory of your ender chest.", + "meteor.command.fake-player.description": "Manages fake players that you can use for testing.", + "meteor.command.fov.description": "Changes your fov.", + "meteor.command.friends.description": "Manages friends.", + "meteor.command.gamemode.description": "Changes your gamemode client-side.", + "meteor.command.give.description": "Gives you any item.", + "meteor.command.hclip.description": "Lets you clip through blocks horizontally.", + "meteor.command.input.description": "Keyboard input simulation.", + "meteor.command.inventory.description": "Allows you to see parts of another player's inventory.", + "meteor.command.locate.description": "Locates structures", + "meteor.command.macro.description": "Allows you to execute macros.", + "meteor.command.modules.description": "Displays a list of all modules.", + "meteor.command.name-history.description": "Provides a list of a players previous names from the laby.net api", + "meteor.command.nbt.description": "Modifies NBT data for an item, example: .nbt add {display:{Name:'{\"text\":\"$cRed Name\"}'}}", + "meteor.command.notebot.description": "Allows you load notebot files", + "meteor.command.peek.description": "Lets you see what's inside storage block items.", + "meteor.command.profiles.description": "Loads and saves profiles.", + "meteor.command.reload.description": "Reloads many systems.", + "meteor.command.reset.description": "Resets specified settings.", + "meteor.command.rotation.description": "Modifies your rotation.", + "meteor.command.save-map.description": "Saves a map to an image.", + "meteor.command.say.description": "Sends messages in chat.", + "meteor.command.server.description": "Prints server information", + "meteor.command.hud.description": "Allows you to view and change module settings.", + "meteor.command.spectate.description": "Allows you to spectate nearby players", + "meteor.command.swarm.description": "Sends commands to connected swarm workers.", + "meteor.command.toggle.description": "Toggles a module.", + "meteor.command.vclip.description": "Lets you clip through blocks vertically.", + "meteor.command.wasp.description": "Sets the auto wasp target.", + "meteor.command.waypoint.description": "Manages waypoints." } From 5634110cf97284c6480998df34480eeb800df482 Mon Sep 17 00:00:00 2001 From: Wide_Cat Date: Mon, 3 Nov 2025 16:10:31 +0000 Subject: [PATCH 05/21] Add a text component for Meteor translations. Translations will be automatically updated when the user changes their language. --- .../meteorclient/commands/Command.java | 5 +- .../commands/commands/CommandsCommand.java | 3 +- .../utils/misc/MeteorTranslations.java | 21 ++++- .../text/MeteorTranslatableTextComponent.java | 76 +++++++++++++++++++ 4 files changed, 97 insertions(+), 8 deletions(-) create mode 100644 src/main/java/meteordevelopment/meteorclient/utils/misc/text/MeteorTranslatableTextComponent.java diff --git a/src/main/java/meteordevelopment/meteorclient/commands/Command.java b/src/main/java/meteordevelopment/meteorclient/commands/Command.java index c61ffc4357..ccb23d2edb 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/Command.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/Command.java @@ -12,7 +12,6 @@ import meteordevelopment.meteorclient.MeteorClient; import meteordevelopment.meteorclient.systems.config.Config; import meteordevelopment.meteorclient.utils.Utils; -import meteordevelopment.meteorclient.utils.misc.MeteorTranslations; import meteordevelopment.meteorclient.utils.player.ChatUtils; import net.minecraft.client.MinecraftClient; import net.minecraft.command.CommandRegistryAccess; @@ -33,7 +32,7 @@ public abstract class Command { private final String description; private final List aliases; - // todo remove in the next minecraft version update + // todo remove the description parameter in the next minecraft version update @Deprecated(forRemoval = true) public Command(String name, String description, String... aliases) { this.name = name; @@ -76,7 +75,7 @@ public String getName() { } public String getDescription() { - return MeteorTranslations.translate(description); + return description; } public List getAliases() { diff --git a/src/main/java/meteordevelopment/meteorclient/commands/commands/CommandsCommand.java b/src/main/java/meteordevelopment/meteorclient/commands/commands/CommandsCommand.java index e35078c472..7202c01151 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/commands/CommandsCommand.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/commands/CommandsCommand.java @@ -10,6 +10,7 @@ import meteordevelopment.meteorclient.commands.Commands; import meteordevelopment.meteorclient.systems.config.Config; import meteordevelopment.meteorclient.utils.Utils; +import meteordevelopment.meteorclient.utils.misc.text.MeteorTranslatableTextComponent; import meteordevelopment.meteorclient.utils.player.ChatUtils; import net.minecraft.command.CommandSource; import net.minecraft.text.ClickEvent; @@ -53,7 +54,7 @@ private MutableText getCommandText(Command command) { } tooltip.append(aliases.formatted(Formatting.GRAY)).append("\n\n"); - tooltip.append(Text.literal(command.getDescription()).formatted(Formatting.WHITE)); + tooltip.append(MutableText.of(new MeteorTranslatableTextComponent(command.getDescription())).formatted(Formatting.WHITE)); // Text MutableText text = Text.literal(Utils.nameToTitle(command.getName())); diff --git a/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java b/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java index 9558feba87..4d6604b538 100644 --- a/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java +++ b/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java @@ -21,6 +21,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; +import java.util.IllegalFormatException; import java.util.List; import java.util.Map; @@ -82,14 +83,26 @@ public static void loadLanguage(String languageCode) { } } - public static String translate(String key) { + public static String translate(String key, Object... args) { MeteorLanguage currentLang = getCurrentLanguage(); - return currentLang.get(key, getDefaultLanguage().get(key)); + String translated = currentLang.get(key, getDefaultLanguage().get(key)); + + try { + return String.format(translated, args); + } catch (IllegalFormatException e) { + return key; + } } - public static String translate(String key, String fallback) { + public static String translate(String key, String fallback, Object... args) { MeteorLanguage currentLang = getCurrentLanguage(); - return currentLang.get(key, getDefaultLanguage().get(key, fallback)); + String translated = currentLang.get(key, getDefaultLanguage().get(key, fallback)); + + try { + return String.format(translated, args); + } catch (IllegalFormatException e) { + return fallback; + } } public static MeteorLanguage getLanguage(String lang) { diff --git a/src/main/java/meteordevelopment/meteorclient/utils/misc/text/MeteorTranslatableTextComponent.java b/src/main/java/meteordevelopment/meteorclient/utils/misc/text/MeteorTranslatableTextComponent.java new file mode 100644 index 0000000000..4313da34f4 --- /dev/null +++ b/src/main/java/meteordevelopment/meteorclient/utils/misc/text/MeteorTranslatableTextComponent.java @@ -0,0 +1,76 @@ +/* + * This file is part of the Meteor Client distribution (https://github.com/MeteorDevelopment/meteor-client). + * Copyright (c) Meteor Development. + */ + +package meteordevelopment.meteorclient.utils.misc.text; + +import com.mojang.serialization.MapCodec; +import meteordevelopment.meteorclient.utils.misc.MeteorTranslations; +import net.minecraft.text.StringVisitable; +import net.minecraft.text.Style; +import net.minecraft.text.TextContent; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.Optional; + +import static meteordevelopment.meteorclient.MeteorClient.mc; + +public class MeteorTranslatableTextComponent implements TextContent { + private final String key; + @Nullable + private final String fallback; + private final Object[] args; + + private String translation; + private String cachedLanguage; + + public MeteorTranslatableTextComponent(String key, @Nullable String fallback, Object... args) { + this.key = key; + this.fallback = fallback; + this.args = args; + } + + public MeteorTranslatableTextComponent(String key) { + this(key, null); + } + + private void updateTranslations() { + if (!mc.options.language.equals(this.cachedLanguage)) { + cachedLanguage = mc.options.language; + translation = fallback == null ? MeteorTranslations.translate(key, args) : MeteorTranslations.translate(key, fallback, args); + } + } + + @Override + public Optional visit(StringVisitable.StyledVisitor visitor, Style style) { + updateTranslations(); + + return visitor.accept(style, translation); + } + + @Override + public Optional visit(StringVisitable.Visitor visitor) { + updateTranslations(); + + return visitor.accept(translation); + } + + @Override + public MapCodec getCodec() { + return null; + } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) return true; + if (!(o instanceof MeteorTranslatableTextComponent component)) return false; + return component.key.equals(this.key) && component.fallback.equals(this.fallback) && Arrays.equals(this.args, component.args); + } + + @Override + public String toString() { + return "MeteorTranslatableTextComponent[key=" + key + ", fallback=" + fallback + ", args=" + Arrays.toString(args) + "]"; + } +} From ffca0cb562c29474c2e66c2bbf9204c1837d9213 Mon Sep 17 00:00:00 2001 From: Wide_Cat Date: Thu, 6 Nov 2025 17:00:40 +0000 Subject: [PATCH 06/21] Assorted fixes and new additions Adds a label in the bottom right corner of the modules screen that can be clicked on to give translation information. --- .../meteorclient/MeteorClient.java | 10 ++++++ .../commands/commands/CommandsCommand.java | 4 +-- .../gui/screens/ModulesScreen.java | 31 +++++++++++++++++++ .../meteorclient/gui/widgets/WWidget.java | 2 +- .../utils/misc/MeteorTranslations.java | 24 +++++++++++--- .../text/MeteorTranslatableTextComponent.java | 7 +++-- .../assets/meteor-client/language/en_gb.json | 2 +- .../assets/meteor-client/language/en_us.json | 2 +- .../assets/meteor-client/language/hi_in.json | 2 ++ .../assets/meteor-client/language/pt_br.json | 2 ++ .../assets/meteor-client/language/vi_vn.json | 2 ++ .../assets/meteor-client/language/zh_cn.json | 2 ++ 12 files changed, 77 insertions(+), 13 deletions(-) diff --git a/src/main/java/meteordevelopment/meteorclient/MeteorClient.java b/src/main/java/meteordevelopment/meteorclient/MeteorClient.java index 2670ade9cc..f2ccda116b 100644 --- a/src/main/java/meteordevelopment/meteorclient/MeteorClient.java +++ b/src/main/java/meteordevelopment/meteorclient/MeteorClient.java @@ -27,6 +27,7 @@ import meteordevelopment.meteorclient.utils.misc.Version; import meteordevelopment.meteorclient.utils.misc.input.KeyAction; import meteordevelopment.meteorclient.utils.misc.input.KeyBinds; +import meteordevelopment.meteorclient.utils.misc.text.MeteorTranslatableTextComponent; import meteordevelopment.meteorclient.utils.network.OnlinePlayers; import meteordevelopment.orbit.EventBus; import meteordevelopment.orbit.EventHandler; @@ -37,6 +38,7 @@ import net.fabricmc.loader.api.metadata.ModMetadata; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.ChatScreen; +import net.minecraft.text.MutableText; import net.minecraft.util.Identifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -197,4 +199,12 @@ private void onOpenScreen(OpenScreenEvent event) { public static Identifier identifier(String path) { return Identifier.of(MeteorClient.MOD_ID, path); } + + public static MutableText translatable(String string, Object... args) { + return MutableText.of(new MeteorTranslatableTextComponent(string, args)); + } + + public static MutableText translatable(String string, String fallback, Object... args) { + return MutableText.of(new MeteorTranslatableTextComponent(string, fallback, args)); + } } diff --git a/src/main/java/meteordevelopment/meteorclient/commands/commands/CommandsCommand.java b/src/main/java/meteordevelopment/meteorclient/commands/commands/CommandsCommand.java index 7202c01151..020fa30178 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/commands/CommandsCommand.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/commands/CommandsCommand.java @@ -6,11 +6,11 @@ package meteordevelopment.meteorclient.commands.commands; import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import meteordevelopment.meteorclient.MeteorClient; import meteordevelopment.meteorclient.commands.Command; import meteordevelopment.meteorclient.commands.Commands; import meteordevelopment.meteorclient.systems.config.Config; import meteordevelopment.meteorclient.utils.Utils; -import meteordevelopment.meteorclient.utils.misc.text.MeteorTranslatableTextComponent; import meteordevelopment.meteorclient.utils.player.ChatUtils; import net.minecraft.command.CommandSource; import net.minecraft.text.ClickEvent; @@ -54,7 +54,7 @@ private MutableText getCommandText(Command command) { } tooltip.append(aliases.formatted(Formatting.GRAY)).append("\n\n"); - tooltip.append(MutableText.of(new MeteorTranslatableTextComponent(command.getDescription())).formatted(Formatting.WHITE)); + tooltip.append(MeteorClient.translatable(command.getDescription())).formatted(Formatting.WHITE); // Text MutableText text = Text.literal(Utils.nameToTitle(command.getName())); diff --git a/src/main/java/meteordevelopment/meteorclient/gui/screens/ModulesScreen.java b/src/main/java/meteordevelopment/meteorclient/gui/screens/ModulesScreen.java index 02c9c0e33a..b953816a8b 100644 --- a/src/main/java/meteordevelopment/meteorclient/gui/screens/ModulesScreen.java +++ b/src/main/java/meteordevelopment/meteorclient/gui/screens/ModulesScreen.java @@ -9,6 +9,7 @@ import meteordevelopment.meteorclient.gui.tabs.TabScreen; import meteordevelopment.meteorclient.gui.tabs.Tabs; import meteordevelopment.meteorclient.gui.utils.Cell; +import meteordevelopment.meteorclient.gui.widgets.WLabel; import meteordevelopment.meteorclient.gui.widgets.containers.WContainer; import meteordevelopment.meteorclient.gui.widgets.containers.WSection; import meteordevelopment.meteorclient.gui.widgets.containers.WVerticalList; @@ -18,13 +19,17 @@ import meteordevelopment.meteorclient.systems.modules.Category; import meteordevelopment.meteorclient.systems.modules.Module; import meteordevelopment.meteorclient.systems.modules.Modules; +import meteordevelopment.meteorclient.utils.misc.MeteorTranslations; import meteordevelopment.meteorclient.utils.misc.NbtUtils; +import meteordevelopment.meteorclient.utils.render.prompts.YesNoPrompt; import net.minecraft.item.Items; +import net.minecraft.util.Util; import java.util.ArrayList; import java.util.List; import java.util.Set; +import static meteordevelopment.meteorclient.MeteorClient.mc; import static meteordevelopment.meteorclient.utils.Utils.getWindowHeight; import static meteordevelopment.meteorclient.utils.Utils.getWindowWidth; @@ -43,6 +48,32 @@ public void initWidgets() { WVerticalList help = add(theme.verticalList()).pad(4).bottom().widget(); help.add(theme.label("Left click - Toggle module")); help.add(theme.label("Right click - Open module settings")); + + // Translation info + if (MeteorTranslations.isEnglish()) return; + double t = MeteorTranslations.percentLocalised(); + + WLabel translation = add(theme.label(String.format("%.1f%% translated", t))).pad(4).bottom().right().widget(); + translation.tooltip = "Open translation info"; + translation.instantTooltips = true; + translation.action = () -> { + // people who translate deserve their names recognised for it + String translators = MeteorTranslations.translate("meteor.lang.translators", ""); + // todo switch this to master before we merge + String url = "https://github.com/MeteorDevelopment/meteor-client/blob/translations/src/main/resources/assets/meteor-client/language/" + mc.options.language.toLowerCase() + ".json"; + + YesNoPrompt prompt = YesNoPrompt.create(theme, this) + .title("Translation Information") + .message("The current language (%s) is currently %.1f%% translated.", mc.options.language, t); + + if (!translators.isEmpty()) prompt.message("Translation work done by: " + translators + ".\n"); + + prompt.message("") + .message("Do you want to open the language file for the current language?") + .onYes(() -> Util.getOperatingSystem().open(url)) + .dontShowAgainCheckboxVisible(false) + .show(); + }; } @Override diff --git a/src/main/java/meteordevelopment/meteorclient/gui/widgets/WWidget.java b/src/main/java/meteordevelopment/meteorclient/gui/widgets/WWidget.java index c3286c2f36..a87b061696 100644 --- a/src/main/java/meteordevelopment/meteorclient/gui/widgets/WWidget.java +++ b/src/main/java/meteordevelopment/meteorclient/gui/widgets/WWidget.java @@ -24,7 +24,7 @@ public abstract class WWidget implements BaseWidget { public String tooltip; public boolean mouseOver; - protected boolean instantTooltips; + public boolean instantTooltips; protected double mouseOverTimer; public void init() {} diff --git a/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java b/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java index 4d6604b538..3b449ac3c7 100644 --- a/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java +++ b/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java @@ -117,16 +117,30 @@ public static MeteorLanguage getDefaultLanguage() { return languages.get("en_us"); } + /** + * @return what percentage of the current language has been localised compared to the default language + */ + public static double percentLocalised() { + // Right now there aren't enough differences between the english dialects to justify each having their own + // translation. Maybe that will change in the future. + if (isEnglish()) return 100; + + double currentLangSize = languages.getOrDefault(mc.options.language.toLowerCase(), new MeteorLanguage()).translations.size(); + return (currentLangSize / getDefaultLanguage().translations.size()) * 100; + } + + public static boolean isEnglish() { + return mc.options.language.toLowerCase().startsWith("en"); + } + public static class MeteorLanguage extends Language { - private final Map translations; + private final Map translations = new Object2ObjectOpenHashMap<>(); private final List> customTranslations = new ObjectArrayList<>(); - public MeteorLanguage() { - this.translations = new Object2ObjectOpenHashMap<>(); - } + public MeteorLanguage() {} public MeteorLanguage(Map translations) { - this.translations = translations; + this.translations.putAll(translations); } public void addCustomTranslation(Map customTranslation) { diff --git a/src/main/java/meteordevelopment/meteorclient/utils/misc/text/MeteorTranslatableTextComponent.java b/src/main/java/meteordevelopment/meteorclient/utils/misc/text/MeteorTranslatableTextComponent.java index 4313da34f4..10f568660e 100644 --- a/src/main/java/meteordevelopment/meteorclient/utils/misc/text/MeteorTranslatableTextComponent.java +++ b/src/main/java/meteordevelopment/meteorclient/utils/misc/text/MeteorTranslatableTextComponent.java @@ -13,6 +13,7 @@ import org.jetbrains.annotations.Nullable; import java.util.Arrays; +import java.util.Objects; import java.util.Optional; import static meteordevelopment.meteorclient.MeteorClient.mc; @@ -32,8 +33,8 @@ public MeteorTranslatableTextComponent(String key, @Nullable String fallback, Ob this.args = args; } - public MeteorTranslatableTextComponent(String key) { - this(key, null); + public MeteorTranslatableTextComponent(String key, Object... args) { + this(key, null, args); } private void updateTranslations() { @@ -66,7 +67,7 @@ public MapCodec getCodec() { public boolean equals(@Nullable Object o) { if (this == o) return true; if (!(o instanceof MeteorTranslatableTextComponent component)) return false; - return component.key.equals(this.key) && component.fallback.equals(this.fallback) && Arrays.equals(this.args, component.args); + return Objects.equals(this.key, component.key) && Objects.equals(this.fallback, component.fallback) && Arrays.equals(this.args, component.args); } @Override diff --git a/src/main/resources/assets/meteor-client/language/en_gb.json b/src/main/resources/assets/meteor-client/language/en_gb.json index 03d3bb501e..ea3d85d40f 100644 --- a/src/main/resources/assets/meteor-client/language/en_gb.json +++ b/src/main/resources/assets/meteor-client/language/en_gb.json @@ -34,7 +34,7 @@ "meteor.command.save-map.description": "Saves a map to an image.", "meteor.command.say.description": "Sends messages in chat.", "meteor.command.server.description": "Prints server information", - "meteor.command.hud.description": "Allows you to view and change module settings.", + "meteor.command.settings.description": "Allows you to view and change module settings.", "meteor.command.spectate.description": "Allows you to spectate nearby players", "meteor.command.swarm.description": "Sends commands to connected swarm workers.", "meteor.command.toggle.description": "Toggles a module.", diff --git a/src/main/resources/assets/meteor-client/language/en_us.json b/src/main/resources/assets/meteor-client/language/en_us.json index 03d3bb501e..ea3d85d40f 100644 --- a/src/main/resources/assets/meteor-client/language/en_us.json +++ b/src/main/resources/assets/meteor-client/language/en_us.json @@ -34,7 +34,7 @@ "meteor.command.save-map.description": "Saves a map to an image.", "meteor.command.say.description": "Sends messages in chat.", "meteor.command.server.description": "Prints server information", - "meteor.command.hud.description": "Allows you to view and change module settings.", + "meteor.command.settings.description": "Allows you to view and change module settings.", "meteor.command.spectate.description": "Allows you to spectate nearby players", "meteor.command.swarm.description": "Sends commands to connected swarm workers.", "meteor.command.toggle.description": "Toggles a module.", diff --git a/src/main/resources/assets/meteor-client/language/hi_in.json b/src/main/resources/assets/meteor-client/language/hi_in.json index 563d10c7b6..47bce958f5 100644 --- a/src/main/resources/assets/meteor-client/language/hi_in.json +++ b/src/main/resources/assets/meteor-client/language/hi_in.json @@ -1,4 +1,6 @@ { + "meteor.lang.translators" : "devendrapoonia", + "meteor.key.open-gui": "GUI खोलें", "meteor.key.open-commands": "कमांड खोलें" } diff --git a/src/main/resources/assets/meteor-client/language/pt_br.json b/src/main/resources/assets/meteor-client/language/pt_br.json index 8a593c9df3..4a3ccf4e0f 100644 --- a/src/main/resources/assets/meteor-client/language/pt_br.json +++ b/src/main/resources/assets/meteor-client/language/pt_br.json @@ -1,4 +1,6 @@ { + "meteor.lang.translators": "Niix-Dan ", + "meteor.key.open-gui": "Abrir Menu", "meteor.key.open-commands": "Abrir Comandos" } diff --git a/src/main/resources/assets/meteor-client/language/vi_vn.json b/src/main/resources/assets/meteor-client/language/vi_vn.json index eec2d74d6c..dfd1122663 100644 --- a/src/main/resources/assets/meteor-client/language/vi_vn.json +++ b/src/main/resources/assets/meteor-client/language/vi_vn.json @@ -1,4 +1,6 @@ { + "meteor.lang.translators": "AnhNguyenlost13", + "meteor.key.open-gui": "Mở giao diện", "meteor.key.open-commands": "Mở lệnh" } diff --git a/src/main/resources/assets/meteor-client/language/zh_cn.json b/src/main/resources/assets/meteor-client/language/zh_cn.json index 6ca1f878e0..1a3140f140 100644 --- a/src/main/resources/assets/meteor-client/language/zh_cn.json +++ b/src/main/resources/assets/meteor-client/language/zh_cn.json @@ -1,4 +1,6 @@ { + "meteor.lang.translators": "Wuqibor, Enaium", + "meteor.key.open-gui": "打开GUI", "meteor.key.open-commands": "输入命令" } From c32946813ded3dd0d407ab1f4c315039bf49e698 Mon Sep 17 00:00:00 2001 From: Wide_Cat Date: Thu, 13 Nov 2025 21:08:07 +0000 Subject: [PATCH 07/21] Moved more strings from commands into the translation files This includes command exceptions and info/warning/error messages. Still more to be done for commands. This is a bit of a rough draft, if you have any ideas on how better to name or format this, open an issue or message me. --- .../meteorclient/MeteorClient.java | 8 ++-- .../meteorclient/commands/Command.java | 26 +++++++----- .../commands/commands/CommandsCommand.java | 3 +- .../commands/commands/DisconnectCommand.java | 2 +- .../commands/commands/DropCommand.java | 12 +++--- .../commands/commands/EnchantCommand.java | 6 +-- .../commands/commands/FakePlayerCommand.java | 7 ++-- .../commands/commands/GiveCommand.java | 6 +-- .../commands/commands/InputCommand.java | 14 +++---- .../commands/commands/MacroCommand.java | 8 ++-- .../commands/commands/NameHistoryCommand.java | 6 +-- .../commands/commands/ProfilesCommand.java | 6 +-- .../commands/commands/ReloadCommand.java | 2 +- .../commands/commands/SaveMapCommand.java | 40 +++++++++---------- .../commands/commands/WaspCommand.java | 6 +-- .../gui/screens/ModulesScreen.java | 2 +- .../mixin/ControlListWidgetMixin.java | 5 +-- .../mixin/KeyBindingCategoryMixin.java | 3 +- .../assets/meteor-client/language/en_gb.json | 32 ++++++++++++++- .../assets/meteor-client/language/en_us.json | 32 ++++++++++++++- .../assets/meteor-client/language/pt_br.json | 2 +- 21 files changed, 145 insertions(+), 83 deletions(-) diff --git a/src/main/java/meteordevelopment/meteorclient/MeteorClient.java b/src/main/java/meteordevelopment/meteorclient/MeteorClient.java index f2ccda116b..7fb667f368 100644 --- a/src/main/java/meteordevelopment/meteorclient/MeteorClient.java +++ b/src/main/java/meteordevelopment/meteorclient/MeteorClient.java @@ -200,11 +200,11 @@ public static Identifier identifier(String path) { return Identifier.of(MeteorClient.MOD_ID, path); } - public static MutableText translatable(String string, Object... args) { - return MutableText.of(new MeteorTranslatableTextComponent(string, args)); + public static MutableText translatable(String key, Object... args) { + return MutableText.of(new MeteorTranslatableTextComponent(key, args)); } - public static MutableText translatable(String string, String fallback, Object... args) { - return MutableText.of(new MeteorTranslatableTextComponent(string, fallback, args)); + public static MutableText translatable(String key, String fallback, Object... args) { + return MutableText.of(new MeteorTranslatableTextComponent(key, fallback, args)); } } diff --git a/src/main/java/meteordevelopment/meteorclient/commands/Command.java b/src/main/java/meteordevelopment/meteorclient/commands/Command.java index ccb23d2edb..424502a0be 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/Command.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/Command.java @@ -12,12 +12,14 @@ import meteordevelopment.meteorclient.MeteorClient; import meteordevelopment.meteorclient.systems.config.Config; import meteordevelopment.meteorclient.utils.Utils; +import meteordevelopment.meteorclient.utils.misc.MeteorTranslations; import meteordevelopment.meteorclient.utils.player.ChatUtils; import net.minecraft.client.MinecraftClient; import net.minecraft.command.CommandRegistryAccess; import net.minecraft.command.CommandSource; import net.minecraft.registry.BuiltinRegistries; import net.minecraft.server.command.CommandManager; +import net.minecraft.text.MutableText; import net.minecraft.text.Text; import java.util.List; @@ -29,23 +31,23 @@ public abstract class Command { private final String name; private final String title; - private final String description; private final List aliases; + public final String translationKey; // todo remove the description parameter in the next minecraft version update @Deprecated(forRemoval = true) public Command(String name, String description, String... aliases) { this.name = name; this.title = Utils.nameToTitle(name); - this.description = "meteor.command." + name + ".description"; this.aliases = List.of(aliases); + this.translationKey = "meteor.command." + name; } public Command(String name) { this.name = name; this.title = Utils.nameToTitle(name); - this.description = "meteor.command." + name + ".description"; this.aliases = List.of(); + this.translationKey = "meteor.command." + name; } // Helper methods to painlessly infer the CommandSource generic type argument @@ -74,10 +76,6 @@ public String getName() { return name; } - public String getDescription() { - return description; - } - public List getAliases() { return aliases; } @@ -99,16 +97,24 @@ public void info(Text message) { public void info(String message, Object... args) { ChatUtils.forceNextPrefixClass(getClass()); - ChatUtils.infoPrefix(title, message, args); + ChatUtils.infoPrefix(title, MeteorTranslations.translate(translationKey + ".info." + message, message, args)); } public void warning(String message, Object... args) { ChatUtils.forceNextPrefixClass(getClass()); - ChatUtils.warningPrefix(title, message, args); + ChatUtils.warningPrefix(title, MeteorTranslations.translate(translationKey + ".warning." + message, message, args)); } public void error(String message, Object... args) { ChatUtils.forceNextPrefixClass(getClass()); - ChatUtils.errorPrefix(title, message, args); + ChatUtils.errorPrefix(title, MeteorTranslations.translate(translationKey + ".error." + message, message, args)); + } + + public MutableText translatable(String string, Object... args) { + return MeteorClient.translatable(translationKey + "." + string, args); + } + + public MutableText translatable(String string, String fallback, Object... args) { + return MeteorClient.translatable(translationKey + "." + string, fallback, args); } } diff --git a/src/main/java/meteordevelopment/meteorclient/commands/commands/CommandsCommand.java b/src/main/java/meteordevelopment/meteorclient/commands/commands/CommandsCommand.java index 020fa30178..ca08d56e89 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/commands/CommandsCommand.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/commands/CommandsCommand.java @@ -6,7 +6,6 @@ package meteordevelopment.meteorclient.commands.commands; import com.mojang.brigadier.builder.LiteralArgumentBuilder; -import meteordevelopment.meteorclient.MeteorClient; import meteordevelopment.meteorclient.commands.Command; import meteordevelopment.meteorclient.commands.Commands; import meteordevelopment.meteorclient.systems.config.Config; @@ -54,7 +53,7 @@ private MutableText getCommandText(Command command) { } tooltip.append(aliases.formatted(Formatting.GRAY)).append("\n\n"); - tooltip.append(MeteorClient.translatable(command.getDescription())).formatted(Formatting.WHITE); + tooltip.append(translatable("description")).formatted(Formatting.WHITE); // Text MutableText text = Text.literal(Utils.nameToTitle(command.getName())); diff --git a/src/main/java/meteordevelopment/meteorclient/commands/commands/DisconnectCommand.java b/src/main/java/meteordevelopment/meteorclient/commands/commands/DisconnectCommand.java index f43585276a..2ae2620ecf 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/commands/DisconnectCommand.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/commands/DisconnectCommand.java @@ -21,7 +21,7 @@ public DisconnectCommand() { @Override public void build(LiteralArgumentBuilder builder) { builder.executes(context -> { - mc.player.networkHandler.onDisconnect(new DisconnectS2CPacket(Text.literal("%s[%sDisconnectCommand%s] Disconnected by user.".formatted(Formatting.GRAY, Formatting.BLUE, Formatting.GRAY)))); + mc.player.networkHandler.onDisconnect(new DisconnectS2CPacket(translatable("disconnection_message", Formatting.GRAY, Formatting.BLUE, Formatting.GRAY))); return SINGLE_SUCCESS; }); diff --git a/src/main/java/meteordevelopment/meteorclient/commands/commands/DropCommand.java b/src/main/java/meteordevelopment/meteorclient/commands/commands/DropCommand.java index e6d7c202c4..e03c181216 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/commands/DropCommand.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/commands/DropCommand.java @@ -10,6 +10,7 @@ import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import meteordevelopment.meteorclient.MeteorClient; import meteordevelopment.meteorclient.commands.Command; import meteordevelopment.meteorclient.utils.player.InvUtils; import net.minecraft.client.network.ClientPlayerEntity; @@ -19,11 +20,10 @@ import net.minecraft.entity.EquipmentSlot; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; -import net.minecraft.text.Text; public class DropCommand extends Command { - private static final SimpleCommandExceptionType NOT_SPECTATOR = new SimpleCommandExceptionType(Text.literal("Can't drop items while in spectator.")); - private static final SimpleCommandExceptionType NO_SUCH_ITEM = new SimpleCommandExceptionType(Text.literal("Could not find an item with that name!")); + private static final SimpleCommandExceptionType NOT_SPECTATOR = new SimpleCommandExceptionType(MeteorClient.translatable("meteor.command.drop.exception.not_spectator")); + private static final SimpleCommandExceptionType NO_SUCH_ITEM = new SimpleCommandExceptionType(MeteorClient.translatable("meteor.command.drop.exception.no_such_item")); public DropCommand() { super("drop"); @@ -70,9 +70,9 @@ public void build(LiteralArgumentBuilder builder) { // Specific item builder.then(argument("item", ItemStackArgumentType.itemStack(REGISTRY_ACCESS)) - .executes(context -> drop(player -> { - dropItem(player, context, Integer.MAX_VALUE); - })) + .executes(context -> drop(player -> + dropItem(player, context, Integer.MAX_VALUE) + )) .then(argument("amount", IntegerArgumentType.integer(1)) .executes(context -> drop(player -> { int amount = IntegerArgumentType.getInteger(context, "amount"); diff --git a/src/main/java/meteordevelopment/meteorclient/commands/commands/EnchantCommand.java b/src/main/java/meteordevelopment/meteorclient/commands/commands/EnchantCommand.java index 6d4f03279a..d9dc46cb3c 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/commands/EnchantCommand.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/commands/EnchantCommand.java @@ -10,6 +10,7 @@ import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import meteordevelopment.meteorclient.MeteorClient; import meteordevelopment.meteorclient.commands.Command; import meteordevelopment.meteorclient.commands.arguments.RegistryEntryReferenceArgumentType; import meteordevelopment.meteorclient.utils.Utils; @@ -19,13 +20,12 @@ import net.minecraft.item.ItemStack; import net.minecraft.registry.RegistryKeys; import net.minecraft.registry.entry.RegistryEntry; -import net.minecraft.text.Text; import java.util.function.ToIntFunction; public class EnchantCommand extends Command { - private static final SimpleCommandExceptionType NOT_IN_CREATIVE = new SimpleCommandExceptionType(Text.literal("You must be in creative mode to use this.")); - private static final SimpleCommandExceptionType NOT_HOLDING_ITEM = new SimpleCommandExceptionType(Text.literal("You need to hold some item to enchant.")); + private static final SimpleCommandExceptionType NOT_IN_CREATIVE = new SimpleCommandExceptionType(MeteorClient.translatable("meteor.command.enchant.exception.not_in_creative")); + private static final SimpleCommandExceptionType NOT_HOLDING_ITEM = new SimpleCommandExceptionType(MeteorClient.translatable("meteor.command.enchant.exception.not_holding_item")); public EnchantCommand() { super("enchant"); diff --git a/src/main/java/meteordevelopment/meteorclient/commands/commands/FakePlayerCommand.java b/src/main/java/meteordevelopment/meteorclient/commands/commands/FakePlayerCommand.java index 8806330029..09dd5e3797 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/commands/FakePlayerCommand.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/commands/FakePlayerCommand.java @@ -13,7 +13,6 @@ import meteordevelopment.meteorclient.systems.modules.player.FakePlayer; import meteordevelopment.meteorclient.utils.entity.fakeplayer.FakePlayerEntity; import meteordevelopment.meteorclient.utils.entity.fakeplayer.FakePlayerManager; -import meteordevelopment.meteorclient.utils.player.ChatUtils; import net.minecraft.command.CommandSource; public class FakePlayerCommand extends Command { @@ -43,12 +42,12 @@ public void build(LiteralArgumentBuilder builder) { .executes(context -> { FakePlayerEntity fp = FakePlayerArgumentType.get(context); if (fp == null || !FakePlayerManager.contains(fp)) { - error("Couldn't find a Fake Player with that name."); + error("not_found"); return SINGLE_SUCCESS; } FakePlayerManager.remove(fp); - info("Removed Fake Player %s.".formatted(fp.getName().getString())); + info("removed", fp.getName().getString()); return SINGLE_SUCCESS; }) @@ -65,7 +64,7 @@ public void build(LiteralArgumentBuilder builder) { builder.then(literal("list") .executes(context -> { info("--- Fake Players ((highlight)%s(default)) ---", FakePlayerManager.count()); - FakePlayerManager.forEach(fp -> ChatUtils.info("(highlight)%s".formatted(fp.getName().getString()))); + FakePlayerManager.forEach(fp -> info("(highlight)%s".formatted(fp.getName().getString()))); return SINGLE_SUCCESS; }) ); diff --git a/src/main/java/meteordevelopment/meteorclient/commands/commands/GiveCommand.java b/src/main/java/meteordevelopment/meteorclient/commands/commands/GiveCommand.java index fa802ede81..83b4e8bc12 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/commands/GiveCommand.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/commands/GiveCommand.java @@ -9,6 +9,7 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import meteordevelopment.meteorclient.MeteorClient; import meteordevelopment.meteorclient.commands.Command; import meteordevelopment.meteorclient.utils.player.FindItemResult; import meteordevelopment.meteorclient.utils.player.InvUtils; @@ -16,11 +17,10 @@ import net.minecraft.command.argument.ItemStackArgumentType; import net.minecraft.item.ItemStack; import net.minecraft.network.packet.c2s.play.CreativeInventoryActionC2SPacket; -import net.minecraft.text.Text; public class GiveCommand extends Command { - private final static SimpleCommandExceptionType NOT_IN_CREATIVE = new SimpleCommandExceptionType(Text.literal("You must be in creative mode to use this.")); - private final static SimpleCommandExceptionType NO_SPACE = new SimpleCommandExceptionType(Text.literal("No space in hotbar.")); + private final static SimpleCommandExceptionType NOT_IN_CREATIVE = new SimpleCommandExceptionType(MeteorClient.translatable("meteor.command.give.exception.not_in_creative")); + private final static SimpleCommandExceptionType NO_SPACE = new SimpleCommandExceptionType(MeteorClient.translatable("meteor.command.give.exception.no_space")); public GiveCommand() { super("give"); diff --git a/src/main/java/meteordevelopment/meteorclient/commands/commands/InputCommand.java b/src/main/java/meteordevelopment/meteorclient/commands/commands/InputCommand.java index c16e3745da..add4cdbd37 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/commands/InputCommand.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/commands/InputCommand.java @@ -76,9 +76,9 @@ public void build(LiteralArgumentBuilder builder) { } builder.then(literal("clear").executes(ctx -> { - if (activeHandlers.isEmpty()) warning("No active keypress handlers."); + if (activeHandlers.isEmpty()) warning("no_handlers"); else { - info("Cleared all keypress handlers."); + info("cleared_handlers"); activeHandlers.forEach(MeteorClient.EVENT_BUS::unsubscribe); activeHandlers.clear(); } @@ -86,12 +86,12 @@ public void build(LiteralArgumentBuilder builder) { })); builder.then(literal("list").executes(ctx -> { - if (activeHandlers.isEmpty()) warning("No active keypress handlers."); + if (activeHandlers.isEmpty()) warning("no_handlers"); else { - info("Active keypress handlers: "); + info(""); for (int i = 0; i < activeHandlers.size(); i++) { KeypressHandler handler = activeHandlers.get(i); - info("(highlight)%d(default) - (highlight)%s %d(default) ticks left out of (highlight)%d(default).", i, I18n.translate(handler.key.getId()), handler.ticks, handler.totalTicks); + info("keypress_handler", i, I18n.translate(handler.key.getId()), handler.ticks, handler.totalTicks); } } return SINGLE_SUCCESS; @@ -99,9 +99,9 @@ public void build(LiteralArgumentBuilder builder) { builder.then(literal("remove").then(argument("index", IntegerArgumentType.integer(0)).executes(ctx -> { int index = IntegerArgumentType.getInteger(ctx, "index"); - if (index >= activeHandlers.size()) warning("Index out of range."); + if (index >= activeHandlers.size()) warning("out_of_range"); else { - info("Removed keypress handler."); + info("removed_handler"); MeteorClient.EVENT_BUS.unsubscribe(activeHandlers.get(index)); activeHandlers.remove(index); } diff --git a/src/main/java/meteordevelopment/meteorclient/commands/commands/MacroCommand.java b/src/main/java/meteordevelopment/meteorclient/commands/commands/MacroCommand.java index 22c899af11..566cfb43e1 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/commands/MacroCommand.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/commands/MacroCommand.java @@ -35,12 +35,12 @@ public void build(LiteralArgumentBuilder builder) { .then(literal("clear") .executes(context -> { if (scheduleQueue.isEmpty() && scheduledMacros.isEmpty()) { - error("No macros are currently scheduled."); + error("none_scheduled"); return SINGLE_SUCCESS; } clearAll(); - info("Cleared all scheduled macros."); + info("cleared_all"); return SINGLE_SUCCESS; }) @@ -49,12 +49,12 @@ public void build(LiteralArgumentBuilder builder) { Macro macro = MacroArgumentType.get(context); if (!isScheduled(macro)) { - error("This macro is not currently scheduled."); + error("not_scheduled"); return SINGLE_SUCCESS; } clear(macro); - info("Cleared scheduled macro."); + info("cleared"); return SINGLE_SUCCESS; }) ) diff --git a/src/main/java/meteordevelopment/meteorclient/commands/commands/NameHistoryCommand.java b/src/main/java/meteordevelopment/meteorclient/commands/commands/NameHistoryCommand.java index db1a317183..3a646a477c 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/commands/NameHistoryCommand.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/commands/NameHistoryCommand.java @@ -38,13 +38,13 @@ public void build(LiteralArgumentBuilder builder) { UUID uuid = lookUpTarget.getProfile().id(); NameHistory history = Http.get("https://laby.net/api/v2/user/" + uuid + "/get-profile") - .exceptionHandler(e -> error("There was an error fetching that users name history.")) + .exceptionHandler(e -> error("error_fetching_name")) .sendJson(NameHistory.class); if (history == null) { return; } else if (history.username_history == null || history.username_history.length == 0) { - error("There was an error fetching that users name history."); + error("error_fetching_name"); } String name = lookUpTarget.getProfile().name(); @@ -85,7 +85,7 @@ public void build(LiteralArgumentBuilder builder) { if (!entry.accurate) { MutableText text = Text.literal("*").formatted(Formatting.WHITE); - text.setStyle(text.getStyle().withHoverEvent(new HoverEvent.ShowText(Text.literal("This name history entry is not accurate according to laby.net")))); + text.setStyle(text.getStyle().withHoverEvent(new HoverEvent.ShowText(translatable("inaccurate")))); nameText.append(text); } diff --git a/src/main/java/meteordevelopment/meteorclient/commands/commands/ProfilesCommand.java b/src/main/java/meteordevelopment/meteorclient/commands/commands/ProfilesCommand.java index fa5ccf9412..f8b0b7c25d 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/commands/ProfilesCommand.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/commands/ProfilesCommand.java @@ -25,7 +25,7 @@ public void build(LiteralArgumentBuilder builder) { if (profile != null) { profile.load(); - info("Loaded profile (highlight)%s(default).", profile.name.get()); + info("loaded", profile.name.get()); } return SINGLE_SUCCESS; @@ -36,7 +36,7 @@ public void build(LiteralArgumentBuilder builder) { if (profile != null) { profile.save(); - info("Saved profile (highlight)%s(default).", profile.name.get()); + info("saved", profile.name.get()); } return SINGLE_SUCCESS; @@ -47,7 +47,7 @@ public void build(LiteralArgumentBuilder builder) { if (profile != null) { Profiles.get().remove(profile); - info("Deleted profile (highlight)%s(default).", profile.name.get()); + info("deleted", profile.name.get()); } return SINGLE_SUCCESS; diff --git a/src/main/java/meteordevelopment/meteorclient/commands/commands/ReloadCommand.java b/src/main/java/meteordevelopment/meteorclient/commands/commands/ReloadCommand.java index 3e7304c5a8..4a66ff2272 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/commands/ReloadCommand.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/commands/ReloadCommand.java @@ -23,7 +23,7 @@ public ReloadCommand() { @Override public void build(LiteralArgumentBuilder builder) { builder.executes(context -> { - warning("Reloading systems, this may take a while."); + warning("reloading"); Systems.load(); Capes.init(); diff --git a/src/main/java/meteordevelopment/meteorclient/commands/commands/SaveMapCommand.java b/src/main/java/meteordevelopment/meteorclient/commands/commands/SaveMapCommand.java index e29c4f2f48..232ed70e9f 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/commands/SaveMapCommand.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/commands/SaveMapCommand.java @@ -19,7 +19,6 @@ import net.minecraft.item.ItemStack; import net.minecraft.item.Items; import net.minecraft.item.map.MapState; -import net.minecraft.text.Text; import org.jetbrains.annotations.Nullable; import org.lwjgl.BufferUtils; import org.lwjgl.PointerBuffer; @@ -33,8 +32,8 @@ import java.nio.ByteBuffer; public class SaveMapCommand extends Command { - private static final SimpleCommandExceptionType MAP_NOT_FOUND = new SimpleCommandExceptionType(Text.literal("You must be holding a filled map.")); - private static final SimpleCommandExceptionType OOPS = new SimpleCommandExceptionType(Text.literal("Something went wrong.")); + private static final SimpleCommandExceptionType MAP_NOT_FOUND = new SimpleCommandExceptionType(MeteorClient.translatable("meteor.command.save-map.exception.map_not_found")); + private static final SimpleCommandExceptionType OOPS = new SimpleCommandExceptionType(MeteorClient.translatable("meteor.command.save-map.exception.oops")); private final PointerBuffer filters; @@ -72,24 +71,25 @@ private void saveMap(int scale) throws CommandSyntaxException { if (path == null) throw OOPS.create(); MapTextureManagerAccessor textureManager = (MapTextureManagerAccessor) mc.gameRenderer.getClient().getMapTextureManager(); - MapTextureManager.MapTexture texture = textureManager.meteor$invokeGetMapTexture(map.get(DataComponentTypes.MAP_ID), state); - if (texture.texture.getImage() == null) throw OOPS.create(); - - try { - if (scale == 128) texture.texture.getImage().writeTo(path); - else { - int[] data = texture.texture.getImage().makePixelArray(); - BufferedImage image = new BufferedImage(128, 128, BufferedImage.TYPE_INT_ARGB); - image.setRGB(0, 0, image.getWidth(), image.getHeight(), data, 0, 128); - - BufferedImage scaledImage = new BufferedImage(scale, scale, 2); - scaledImage.createGraphics().drawImage(image, 0, 0, scale, scale, null); - - ImageIO.write(scaledImage, "png", path); + try (MapTextureManager.MapTexture texture = textureManager.meteor$invokeGetMapTexture(map.get(DataComponentTypes.MAP_ID), state)) { + if (texture.texture.getImage() == null) throw OOPS.create(); + + try { + if (scale == 128) texture.texture.getImage().writeTo(path); + else { + int[] data = texture.texture.getImage().makePixelArray(); + BufferedImage image = new BufferedImage(128, 128, BufferedImage.TYPE_INT_ARGB); + image.setRGB(0, 0, image.getWidth(), image.getHeight(), data, 0, 128); + + BufferedImage scaledImage = new BufferedImage(scale, scale, 2); + scaledImage.createGraphics().drawImage(image, 0, 0, scale, scale, null); + + ImageIO.write(scaledImage, "png", path); + } + } catch (IOException e) { + error("error_writing_texture"); + MeteorClient.LOG.error(e.toString()); } - } catch (IOException e) { - error("Error writing map texture"); - MeteorClient.LOG.error(e.toString()); } } diff --git a/src/main/java/meteordevelopment/meteorclient/commands/commands/WaspCommand.java b/src/main/java/meteordevelopment/meteorclient/commands/commands/WaspCommand.java index d5d9fb15eb..0f57375d76 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/commands/WaspCommand.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/commands/WaspCommand.java @@ -7,16 +7,16 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import meteordevelopment.meteorclient.MeteorClient; import meteordevelopment.meteorclient.commands.Command; import meteordevelopment.meteorclient.commands.arguments.PlayerArgumentType; import meteordevelopment.meteorclient.systems.modules.Modules; import meteordevelopment.meteorclient.systems.modules.movement.AutoWasp; import net.minecraft.command.CommandSource; import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.text.Text; public class WaspCommand extends Command { - private static final SimpleCommandExceptionType CANT_WASP_SELF = new SimpleCommandExceptionType(Text.literal("You cannot target yourself!")); + private static final SimpleCommandExceptionType CANT_WASP_SELF = new SimpleCommandExceptionType(MeteorClient.translatable("meteor.command.wasp.exception.cant_wasp_self")); public WaspCommand() { super("wasp"); @@ -38,7 +38,7 @@ public void build(LiteralArgumentBuilder builder) { wasp.target = player; if (!wasp.isActive()) wasp.toggle(); - info(player.getName().getString() + " set as target."); + info("target", player.getName().getString()); return SINGLE_SUCCESS; })); } diff --git a/src/main/java/meteordevelopment/meteorclient/gui/screens/ModulesScreen.java b/src/main/java/meteordevelopment/meteorclient/gui/screens/ModulesScreen.java index b953816a8b..bd1f0bfbd3 100644 --- a/src/main/java/meteordevelopment/meteorclient/gui/screens/ModulesScreen.java +++ b/src/main/java/meteordevelopment/meteorclient/gui/screens/ModulesScreen.java @@ -66,7 +66,7 @@ public void initWidgets() { .title("Translation Information") .message("The current language (%s) is currently %.1f%% translated.", mc.options.language, t); - if (!translators.isEmpty()) prompt.message("Translation work done by: " + translators + ".\n"); + if (!translators.isEmpty()) prompt.message("Translation work done by: " + translators); prompt.message("") .message("Do you want to open the language file for the current language?") diff --git a/src/main/java/meteordevelopment/meteorclient/mixin/ControlListWidgetMixin.java b/src/main/java/meteordevelopment/meteorclient/mixin/ControlListWidgetMixin.java index 416e592ad6..40bcc25ae7 100644 --- a/src/main/java/meteordevelopment/meteorclient/mixin/ControlListWidgetMixin.java +++ b/src/main/java/meteordevelopment/meteorclient/mixin/ControlListWidgetMixin.java @@ -7,11 +7,10 @@ import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import com.llamalad7.mixinextras.sugar.Local; -import meteordevelopment.meteorclient.utils.misc.MeteorTranslations; +import meteordevelopment.meteorclient.MeteorClient; import net.minecraft.client.gui.screen.option.ControlsListWidget; import net.minecraft.client.option.KeyBinding; import net.minecraft.text.MutableText; -import net.minecraft.text.Text; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @@ -19,7 +18,7 @@ public class ControlListWidgetMixin { @ModifyExpressionValue(method = "", at = @At(value = "INVOKE", target = "Lnet/minecraft/text/Text;translatable(Ljava/lang/String;)Lnet/minecraft/text/MutableText;")) private MutableText modifyText(MutableText original, @Local KeyBinding binding) { - if (binding.getId().startsWith("meteor.key.")) return Text.literal(MeteorTranslations.translate(binding.getId())); + if (binding.getId().startsWith("meteor.key.")) return MeteorClient.translatable(binding.getId()); return original; } } diff --git a/src/main/java/meteordevelopment/meteorclient/mixin/KeyBindingCategoryMixin.java b/src/main/java/meteordevelopment/meteorclient/mixin/KeyBindingCategoryMixin.java index 766945a158..fcb1709e42 100644 --- a/src/main/java/meteordevelopment/meteorclient/mixin/KeyBindingCategoryMixin.java +++ b/src/main/java/meteordevelopment/meteorclient/mixin/KeyBindingCategoryMixin.java @@ -7,7 +7,6 @@ import com.llamalad7.mixinextras.injector.ModifyReturnValue; import meteordevelopment.meteorclient.MeteorClient; -import meteordevelopment.meteorclient.utils.misc.MeteorTranslations; import net.minecraft.client.option.KeyBinding; import net.minecraft.text.Text; import net.minecraft.util.Identifier; @@ -24,7 +23,7 @@ public class KeyBindingCategoryMixin { @ModifyReturnValue(method = "getLabel", at = @At("RETURN")) private Text modifyLabel(Text original) { - if (id.getNamespace().equals(MeteorClient.MOD_ID)) return Text.literal(MeteorTranslations.translate(id.getPath())); + if (id.getNamespace().equals(MeteorClient.MOD_ID)) return MeteorClient.translatable(id.getPath()); return original; } } diff --git a/src/main/resources/assets/meteor-client/language/en_gb.json b/src/main/resources/assets/meteor-client/language/en_gb.json index ea3d85d40f..35aa0b8e0c 100644 --- a/src/main/resources/assets/meteor-client/language/en_gb.json +++ b/src/main/resources/assets/meteor-client/language/en_gb.json @@ -1,37 +1,65 @@ { "meteor.key.category": "Meteor Client", - "meteor.key.open-gui": "Open GUI", "meteor.key.open-commands": "Open Commands", + "meteor.key.open-gui": "Open GUI", "meteor.command.bind.description": "Binds a specified module to the next pressed key.", "meteor.command.binds.description": "List of all bound modules.", "meteor.command.commands.description": "List of all commands.", "meteor.command.damage.description": "Damages self.", "meteor.command.disconnect.description": "Disconnect from the server", + "meteor.command.disconnect.disconnection_message": "%s[%sDisconnectCommand%s] Disconnected by user.", "meteor.command.dismount.description": "Dismounts you from entity you are riding.", "meteor.command.drop.description": "Automatically drops specified items.", + "meteor.command.drop.exception.no_such_item": "Could not find an item with that name!", + "meteor.command.drop.exception.not_spectator": "Can't drop items while in spectator.", "meteor.command.enchant.description": "Enchants the item in your hand. REQUIRES Creative mode.", + "meteor.command.enchant.exception.not_holding_item": "You need to hold some item to enchant.", + "meteor.command.enchant.exception.not_in_creative": "You must be in creative mode to use this.", "meteor.command.ender-chest.description": "Allows you to preview memory of your ender chest.", "meteor.command.fake-player.description": "Manages fake players that you can use for testing.", + "meteor.command.fake-player.error.not_found": "Couldn't find a Fake Player with that name.", + "meteor.command.fake-player.info.removed": "Removed Fake Player %s.", "meteor.command.fov.description": "Changes your fov.", "meteor.command.friends.description": "Manages friends.", "meteor.command.gamemode.description": "Changes your gamemode client-side.", "meteor.command.give.description": "Gives you any item.", + "meteor.command.give.exception.not_in_creative": "You must be in creative mode to use this.", + "meteor.command.give.exception.no_space": "No space in hotbar.", "meteor.command.hclip.description": "Lets you clip through blocks horizontally.", "meteor.command.input.description": "Keyboard input simulation.", + "meteor.command.input.info.cleared_handlers": "Cleared all keypress handlers.", + "meteor.command.input.info.active_handlers": "Active keypress handlers: ", + "meteor.command.input.info.keypress_handler": "(highlight)%d(default) - (highlight)%s %d(default) ticks left out of (highlight)%d(default).", + "meteor.command.input.info.removed_handler": "Removed keypress handler.", + "meteor.command.input.warning.no_handlers": "No active keypress handlers.", + "meteor.command.input.warning.out_of_range": "Index out of range.", "meteor.command.inventory.description": "Allows you to see parts of another player's inventory.", "meteor.command.locate.description": "Locates structures", "meteor.command.macro.description": "Allows you to execute macros.", + "meteor.command.macro.error.none_scheduled": "No macros are currently scheduled.", + "meteor.command.macro.error.not_scheduled": "This macro is not currently scheduled.", + "meteor.command.macro.info.cleared_all": "Cleared all scheduled macros.", + "meteor.command.macro.info.cleared": "Cleared scheduled macro.", "meteor.command.modules.description": "Displays a list of all modules.", "meteor.command.name-history.description": "Provides a list of a players previous names from the laby.net api", + "meteor.command.name-history.error.error_fetching_name": "There was an error fetching that users name history.", + "meteor.command.name-history.inaccurate": "This name history entry is not accurate according to laby.net", "meteor.command.nbt.description": "Modifies NBT data for an item, example: .nbt add {display:{Name:'{\"text\":\"$cRed Name\"}'}}", "meteor.command.notebot.description": "Allows you load notebot files", "meteor.command.peek.description": "Lets you see what's inside storage block items.", "meteor.command.profiles.description": "Loads and saves profiles.", + "meteor.command.profiles.info.loaded": "Loaded profile (highlight)%s(default).", + "meteor.command.profiles.info.saved": "Saved profile (highlight)%s(default).", + "meteor.command.profiles.info.deleted": "Deleted profile (highlight)%s(default).", "meteor.command.reload.description": "Reloads many systems.", + "meteor.command.reload.warning.reloading": "Reloading systems, this may take a while.", "meteor.command.reset.description": "Resets specified settings.", "meteor.command.rotation.description": "Modifies your rotation.", "meteor.command.save-map.description": "Saves a map to an image.", + "meteor.command.save-map.error.error_writing_texture": "Error writing map texture", + "meteor.command.save-map.exception.map_not_found": "You must be holding a filled map.", + "meteor.command.save-map.exception.oops": "Something went wrong.", "meteor.command.say.description": "Sends messages in chat.", "meteor.command.server.description": "Prints server information", "meteor.command.settings.description": "Allows you to view and change module settings.", @@ -40,5 +68,7 @@ "meteor.command.toggle.description": "Toggles a module.", "meteor.command.vclip.description": "Lets you clip through blocks vertically.", "meteor.command.wasp.description": "Sets the auto wasp target.", + "meteor.command.wasp.exception.cant_wasp_self": "You cannot target yourself!", + "meteor.command.wasp.info.target": "%d set as target.", "meteor.command.waypoint.description": "Manages waypoints." } diff --git a/src/main/resources/assets/meteor-client/language/en_us.json b/src/main/resources/assets/meteor-client/language/en_us.json index ea3d85d40f..35aa0b8e0c 100644 --- a/src/main/resources/assets/meteor-client/language/en_us.json +++ b/src/main/resources/assets/meteor-client/language/en_us.json @@ -1,37 +1,65 @@ { "meteor.key.category": "Meteor Client", - "meteor.key.open-gui": "Open GUI", "meteor.key.open-commands": "Open Commands", + "meteor.key.open-gui": "Open GUI", "meteor.command.bind.description": "Binds a specified module to the next pressed key.", "meteor.command.binds.description": "List of all bound modules.", "meteor.command.commands.description": "List of all commands.", "meteor.command.damage.description": "Damages self.", "meteor.command.disconnect.description": "Disconnect from the server", + "meteor.command.disconnect.disconnection_message": "%s[%sDisconnectCommand%s] Disconnected by user.", "meteor.command.dismount.description": "Dismounts you from entity you are riding.", "meteor.command.drop.description": "Automatically drops specified items.", + "meteor.command.drop.exception.no_such_item": "Could not find an item with that name!", + "meteor.command.drop.exception.not_spectator": "Can't drop items while in spectator.", "meteor.command.enchant.description": "Enchants the item in your hand. REQUIRES Creative mode.", + "meteor.command.enchant.exception.not_holding_item": "You need to hold some item to enchant.", + "meteor.command.enchant.exception.not_in_creative": "You must be in creative mode to use this.", "meteor.command.ender-chest.description": "Allows you to preview memory of your ender chest.", "meteor.command.fake-player.description": "Manages fake players that you can use for testing.", + "meteor.command.fake-player.error.not_found": "Couldn't find a Fake Player with that name.", + "meteor.command.fake-player.info.removed": "Removed Fake Player %s.", "meteor.command.fov.description": "Changes your fov.", "meteor.command.friends.description": "Manages friends.", "meteor.command.gamemode.description": "Changes your gamemode client-side.", "meteor.command.give.description": "Gives you any item.", + "meteor.command.give.exception.not_in_creative": "You must be in creative mode to use this.", + "meteor.command.give.exception.no_space": "No space in hotbar.", "meteor.command.hclip.description": "Lets you clip through blocks horizontally.", "meteor.command.input.description": "Keyboard input simulation.", + "meteor.command.input.info.cleared_handlers": "Cleared all keypress handlers.", + "meteor.command.input.info.active_handlers": "Active keypress handlers: ", + "meteor.command.input.info.keypress_handler": "(highlight)%d(default) - (highlight)%s %d(default) ticks left out of (highlight)%d(default).", + "meteor.command.input.info.removed_handler": "Removed keypress handler.", + "meteor.command.input.warning.no_handlers": "No active keypress handlers.", + "meteor.command.input.warning.out_of_range": "Index out of range.", "meteor.command.inventory.description": "Allows you to see parts of another player's inventory.", "meteor.command.locate.description": "Locates structures", "meteor.command.macro.description": "Allows you to execute macros.", + "meteor.command.macro.error.none_scheduled": "No macros are currently scheduled.", + "meteor.command.macro.error.not_scheduled": "This macro is not currently scheduled.", + "meteor.command.macro.info.cleared_all": "Cleared all scheduled macros.", + "meteor.command.macro.info.cleared": "Cleared scheduled macro.", "meteor.command.modules.description": "Displays a list of all modules.", "meteor.command.name-history.description": "Provides a list of a players previous names from the laby.net api", + "meteor.command.name-history.error.error_fetching_name": "There was an error fetching that users name history.", + "meteor.command.name-history.inaccurate": "This name history entry is not accurate according to laby.net", "meteor.command.nbt.description": "Modifies NBT data for an item, example: .nbt add {display:{Name:'{\"text\":\"$cRed Name\"}'}}", "meteor.command.notebot.description": "Allows you load notebot files", "meteor.command.peek.description": "Lets you see what's inside storage block items.", "meteor.command.profiles.description": "Loads and saves profiles.", + "meteor.command.profiles.info.loaded": "Loaded profile (highlight)%s(default).", + "meteor.command.profiles.info.saved": "Saved profile (highlight)%s(default).", + "meteor.command.profiles.info.deleted": "Deleted profile (highlight)%s(default).", "meteor.command.reload.description": "Reloads many systems.", + "meteor.command.reload.warning.reloading": "Reloading systems, this may take a while.", "meteor.command.reset.description": "Resets specified settings.", "meteor.command.rotation.description": "Modifies your rotation.", "meteor.command.save-map.description": "Saves a map to an image.", + "meteor.command.save-map.error.error_writing_texture": "Error writing map texture", + "meteor.command.save-map.exception.map_not_found": "You must be holding a filled map.", + "meteor.command.save-map.exception.oops": "Something went wrong.", "meteor.command.say.description": "Sends messages in chat.", "meteor.command.server.description": "Prints server information", "meteor.command.settings.description": "Allows you to view and change module settings.", @@ -40,5 +68,7 @@ "meteor.command.toggle.description": "Toggles a module.", "meteor.command.vclip.description": "Lets you clip through blocks vertically.", "meteor.command.wasp.description": "Sets the auto wasp target.", + "meteor.command.wasp.exception.cant_wasp_self": "You cannot target yourself!", + "meteor.command.wasp.info.target": "%d set as target.", "meteor.command.waypoint.description": "Manages waypoints." } diff --git a/src/main/resources/assets/meteor-client/language/pt_br.json b/src/main/resources/assets/meteor-client/language/pt_br.json index 4a3ccf4e0f..16cc5e0c1c 100644 --- a/src/main/resources/assets/meteor-client/language/pt_br.json +++ b/src/main/resources/assets/meteor-client/language/pt_br.json @@ -1,5 +1,5 @@ { - "meteor.lang.translators": "Niix-Dan ", + "meteor.lang.translators": "Niix-Dan", "meteor.key.open-gui": "Abrir Menu", "meteor.key.open-commands": "Abrir Comandos" From 2f8f05331c83fbc072404330afff98b7c8a2d1c1 Mon Sep 17 00:00:00 2001 From: RacoonDog <32882447+racoondog@users.noreply.github.com> Date: Tue, 18 Nov 2025 00:05:28 -0500 Subject: [PATCH 08/21] add error message when IOExcepting during en_us --- .../meteorclient/utils/misc/MeteorTranslations.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java b/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java index 3b449ac3c7..e4b396f522 100644 --- a/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java +++ b/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java @@ -60,7 +60,7 @@ public static void loadLanguage(String languageCode) { MeteorClient.LOG.info("Loaded language: {}", languageCode); } } catch (IOException e) { - if (languageCode.equals("en_us")) throw new RuntimeException(e); + if (languageCode.equals("en_us")) throw new RuntimeException("Error loading default language", e); else MeteorClient.LOG.error("Error loading language: {}", languageCode, e); } From 9aed686bb70054a3651bac147d4e40e7d40f2a0f Mon Sep 17 00:00:00 2001 From: RacoonDog <32882447+racoondog@users.noreply.github.com> Date: Tue, 18 Nov 2025 00:06:14 -0500 Subject: [PATCH 09/21] always use utf8 for translations --- .../meteorclient/utils/misc/MeteorTranslations.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java b/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java index e4b396f522..17233e495d 100644 --- a/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java +++ b/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.IllegalFormatException; import java.util.List; @@ -54,7 +55,7 @@ public static void loadLanguage(String languageCode) { } else { // noinspection unchecked - Object2ObjectOpenHashMap map = GSON.fromJson(new InputStreamReader(stream), Object2ObjectOpenHashMap.class); + Object2ObjectOpenHashMap map = GSON.fromJson(new InputStreamReader(stream, StandardCharsets.UTF_8), Object2ObjectOpenHashMap.class); languages.put(languageCode, new MeteorLanguage(map)); MeteorClient.LOG.info("Loaded language: {}", languageCode); @@ -72,7 +73,7 @@ public static void loadLanguage(String languageCode) { MeteorLanguage lang = languages.getOrDefault(languageCode, new MeteorLanguage()); // noinspection unchecked - Object2ObjectOpenHashMap map = GSON.fromJson(new InputStreamReader(stream), Object2ObjectOpenHashMap.class); + Object2ObjectOpenHashMap map = GSON.fromJson(new InputStreamReader(stream, StandardCharsets.UTF_8), Object2ObjectOpenHashMap.class); lang.addCustomTranslation(map); languages.put(languageCode, lang); From 6408e378b65f98a1dd8069a35b2401bdadd20057 Mon Sep 17 00:00:00 2001 From: RacoonDog <32882447+racoondog@users.noreply.github.com> Date: Tue, 18 Nov 2025 00:18:41 -0500 Subject: [PATCH 10/21] set default lang as a constant --- .../utils/misc/MeteorTranslations.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java b/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java index 17233e495d..82f713810c 100644 --- a/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java +++ b/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java @@ -30,18 +30,22 @@ @SuppressWarnings("unused") public class MeteorTranslations { + private static final String EN_US_CODE = "en_us"; private static final Gson GSON = new Gson(); private static final Map languages = new Object2ObjectOpenHashMap<>(); + private static MeteorLanguage defaultLanguage; @PreInit public static void preInit() { List toLoad = new ArrayList<>(2); - toLoad.add("en_us"); - if (!mc.options.language.equalsIgnoreCase("en_us")) toLoad.add(mc.options.language); + toLoad.add(EN_US_CODE); + if (!mc.options.language.equalsIgnoreCase(EN_US_CODE)) toLoad.add(mc.options.language); for (String language : toLoad) { loadLanguage(language); } + + defaultLanguage = getLanguage(EN_US_CODE); } public static void loadLanguage(String languageCode) { @@ -50,7 +54,7 @@ public static void loadLanguage(String languageCode) { try (InputStream stream = MeteorTranslations.class.getResourceAsStream("/assets/meteor-client/language/" + languageCode + ".json")) { if (stream == null) { - if (languageCode.equals("en_us")) throw new RuntimeException("Error loading the default language"); + if (languageCode.equals(EN_US_CODE)) throw new RuntimeException("Error loading the default language"); else MeteorClient.LOG.info("No language file found for '{}'", languageCode); } else { @@ -61,7 +65,7 @@ public static void loadLanguage(String languageCode) { MeteorClient.LOG.info("Loaded language: {}", languageCode); } } catch (IOException e) { - if (languageCode.equals("en_us")) throw new RuntimeException("Error loading default language", e); + if (languageCode.equals(EN_US_CODE)) throw new RuntimeException("Error loading default language", e); else MeteorClient.LOG.error("Error loading language: {}", languageCode, e); } @@ -115,7 +119,7 @@ public static MeteorLanguage getCurrentLanguage() { } public static MeteorLanguage getDefaultLanguage() { - return languages.get("en_us"); + return defaultLanguage; } /** From c15102aa7853769e5fc0f8ccbd8d1ef0a3155abc Mon Sep 17 00:00:00 2001 From: RacoonDog <32882447+racoondog@users.noreply.github.com> Date: Tue, 18 Nov 2025 00:42:58 -0500 Subject: [PATCH 11/21] add id field to `MeteorAddon` --- .../meteordevelopment/meteorclient/addons/AddonManager.java | 2 ++ .../meteordevelopment/meteorclient/addons/MeteorAddon.java | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/src/main/java/meteordevelopment/meteorclient/addons/AddonManager.java b/src/main/java/meteordevelopment/meteorclient/addons/AddonManager.java index 54af1c4d00..2e1a10668e 100644 --- a/src/main/java/meteordevelopment/meteorclient/addons/AddonManager.java +++ b/src/main/java/meteordevelopment/meteorclient/addons/AddonManager.java @@ -48,6 +48,7 @@ public String getCommit() { ModMetadata metadata = FabricLoader.getInstance().getModContainer(MeteorClient.MOD_ID).get().getMetadata(); + MeteorClient.ADDON.id = metadata.getId(); MeteorClient.ADDON.name = metadata.getName(); MeteorClient.ADDON.authors = new String[metadata.getAuthors().size()]; if (metadata.containsCustomValue(MeteorClient.MOD_ID + ":color")) { @@ -72,6 +73,7 @@ public String getCommit() { throw new RuntimeException("Exception during addon init \"%s\".".formatted(metadata.getName()), throwable); } + addon.id = metadata.getId(); addon.name = metadata.getName(); if (metadata.getAuthors().isEmpty()) throw new RuntimeException("Addon \"%s\" requires at least 1 author to be defined in it's fabric.mod.json. See https://fabricmc.net/wiki/documentation:fabric_mod_json_spec".formatted(addon.name)); diff --git a/src/main/java/meteordevelopment/meteorclient/addons/MeteorAddon.java b/src/main/java/meteordevelopment/meteorclient/addons/MeteorAddon.java index 7ae5918d6f..01286ead0f 100644 --- a/src/main/java/meteordevelopment/meteorclient/addons/MeteorAddon.java +++ b/src/main/java/meteordevelopment/meteorclient/addons/MeteorAddon.java @@ -10,6 +10,10 @@ import java.io.InputStream; public abstract class MeteorAddon { + /** This field is automatically assigned from fabric.mod.json file. + * @since 1.21.11 */ // todo replace with exact version when released + public String id; + /** This field is automatically assigned from fabric.mod.json file. */ public String name; From 109b6ef99e52301396190b040b24c85550718cfc Mon Sep 17 00:00:00 2001 From: RacoonDog <32882447+racoondog@users.noreply.github.com> Date: Tue, 18 Nov 2025 00:50:09 -0500 Subject: [PATCH 12/21] compute map of localized languages --- .../utils/misc/MeteorTranslations.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java b/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java index 82f713810c..0ce3709542 100644 --- a/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java +++ b/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java @@ -12,6 +12,8 @@ import meteordevelopment.meteorclient.addons.AddonManager; import meteordevelopment.meteorclient.addons.MeteorAddon; import meteordevelopment.meteorclient.utils.PreInit; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.resource.language.LanguageDefinition; import net.minecraft.client.resource.language.ReorderingUtil; import net.minecraft.text.OrderedText; import net.minecraft.text.StringVisitable; @@ -33,10 +35,17 @@ public class MeteorTranslations { private static final String EN_US_CODE = "en_us"; private static final Gson GSON = new Gson(); private static final Map languages = new Object2ObjectOpenHashMap<>(); + private static final Map languageDefinitions = new Object2ObjectOpenHashMap<>(); private static MeteorLanguage defaultLanguage; @PreInit public static void preInit() { + MinecraftClient.getInstance().getLanguageManager().getAllLanguages().forEach((code, definition) -> { + if (hasLocalization(code)) { + languageDefinitions.put(code, definition); + } + }); + List toLoad = new ArrayList<>(2); toLoad.add(EN_US_CODE); if (!mc.options.language.equalsIgnoreCase(EN_US_CODE)) toLoad.add(mc.options.language); @@ -48,6 +57,20 @@ public static void preInit() { defaultLanguage = getLanguage(EN_US_CODE); } + private static boolean hasLocalization(String languageCode) { + if (doesLangFileExist(MeteorClient.ADDON, languageCode)) return true; + + for (MeteorAddon addon : AddonManager.ADDONS) { + if (doesLangFileExist(addon, languageCode)) return true; + } + + return false; + } + + private static boolean doesLangFileExist(MeteorAddon addon, String languageCode) { + return addon.getClass().getResource("/assets/" + addon.id + "/language/" + languageCode + ".json") != null; + } + public static void loadLanguage(String languageCode) { languageCode = languageCode.toLowerCase(); if (languages.containsKey(languageCode)) return; @@ -110,6 +133,10 @@ public static String translate(String key, String fallback, Object... args) { } } + public static Map getLanguageDefinitions() { + return languageDefinitions; + } + public static MeteorLanguage getLanguage(String lang) { return languages.get(lang); } From 9b1cfdba58ef89c0f38bb869debcc197c6f0bc55 Mon Sep 17 00:00:00 2001 From: RacoonDog <32882447+racoondog@users.noreply.github.com> Date: Tue, 18 Nov 2025 00:52:56 -0500 Subject: [PATCH 13/21] add right-to-left support --- .../utils/misc/MeteorTranslations.java | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java b/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java index 0ce3709542..89a7f86b29 100644 --- a/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java +++ b/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java @@ -75,6 +75,9 @@ public static void loadLanguage(String languageCode) { languageCode = languageCode.toLowerCase(); if (languages.containsKey(languageCode)) return; + LanguageDefinition definition = languageDefinitions.get(languageCode); + if (definition == null) return; + try (InputStream stream = MeteorTranslations.class.getResourceAsStream("/assets/meteor-client/language/" + languageCode + ".json")) { if (stream == null) { if (languageCode.equals(EN_US_CODE)) throw new RuntimeException("Error loading the default language"); @@ -83,7 +86,7 @@ public static void loadLanguage(String languageCode) { else { // noinspection unchecked Object2ObjectOpenHashMap map = GSON.fromJson(new InputStreamReader(stream, StandardCharsets.UTF_8), Object2ObjectOpenHashMap.class); - languages.put(languageCode, new MeteorLanguage(map)); + languages.put(languageCode, new MeteorLanguage(definition.rightToLeft(), map)); MeteorClient.LOG.info("Loaded language: {}", languageCode); } @@ -97,7 +100,7 @@ public static void loadLanguage(String languageCode) { try (InputStream stream = addon.provideLanguage(languageCode)) { if (stream == null) continue; - MeteorLanguage lang = languages.getOrDefault(languageCode, new MeteorLanguage()); + MeteorLanguage lang = languages.getOrDefault(languageCode, new MeteorLanguage(definition.rightToLeft())); // noinspection unchecked Object2ObjectOpenHashMap map = GSON.fromJson(new InputStreamReader(stream, StandardCharsets.UTF_8), Object2ObjectOpenHashMap.class); @@ -157,7 +160,7 @@ public static double percentLocalised() { // translation. Maybe that will change in the future. if (isEnglish()) return 100; - double currentLangSize = languages.getOrDefault(mc.options.language.toLowerCase(), new MeteorLanguage()).translations.size(); + double currentLangSize = languages.getOrDefault(mc.options.language.toLowerCase(), new MeteorLanguage(false)).translations.size(); return (currentLangSize / getDefaultLanguage().translations.size()) * 100; } @@ -168,10 +171,14 @@ public static boolean isEnglish() { public static class MeteorLanguage extends Language { private final Map translations = new Object2ObjectOpenHashMap<>(); private final List> customTranslations = new ObjectArrayList<>(); + private final boolean rightToLeft; - public MeteorLanguage() {} + public MeteorLanguage(boolean rightToLeft) { + this.rightToLeft = rightToLeft; + } - public MeteorLanguage(Map translations) { + public MeteorLanguage(boolean rightToLeft, Map translations) { + this(rightToLeft); this.translations.putAll(translations); } @@ -205,12 +212,12 @@ public boolean hasTranslation(String key) { @Override public boolean isRightToLeft() { - return false; + return this.rightToLeft; } @Override public OrderedText reorder(StringVisitable text) { - return ReorderingUtil.reorder(text, false); + return ReorderingUtil.reorder(text, this.rightToLeft); } } } From 1dd7566498bde476a75cf613101a7feb8c214c15 Mon Sep 17 00:00:00 2001 From: RacoonDog <32882447+racoondog@users.noreply.github.com> Date: Tue, 18 Nov 2025 01:08:48 -0500 Subject: [PATCH 14/21] flatten translation maps --- .../utils/misc/MeteorTranslations.java | 45 ++++++------------- 1 file changed, 13 insertions(+), 32 deletions(-) diff --git a/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java b/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java index 89a7f86b29..6c1f314099 100644 --- a/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java +++ b/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java @@ -7,7 +7,6 @@ import com.google.gson.Gson; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; import meteordevelopment.meteorclient.MeteorClient; import meteordevelopment.meteorclient.addons.AddonManager; import meteordevelopment.meteorclient.addons.MeteorAddon; @@ -78,6 +77,8 @@ public static void loadLanguage(String languageCode) { LanguageDefinition definition = languageDefinitions.get(languageCode); if (definition == null) return; + Object2ObjectOpenHashMap languageMap = new Object2ObjectOpenHashMap<>(); + try (InputStream stream = MeteorTranslations.class.getResourceAsStream("/assets/meteor-client/language/" + languageCode + ".json")) { if (stream == null) { if (languageCode.equals(EN_US_CODE)) throw new RuntimeException("Error loading the default language"); @@ -86,7 +87,7 @@ public static void loadLanguage(String languageCode) { else { // noinspection unchecked Object2ObjectOpenHashMap map = GSON.fromJson(new InputStreamReader(stream, StandardCharsets.UTF_8), Object2ObjectOpenHashMap.class); - languages.put(languageCode, new MeteorLanguage(definition.rightToLeft(), map)); + languageMap.putAll(map); MeteorClient.LOG.info("Loaded language: {}", languageCode); } @@ -100,18 +101,20 @@ public static void loadLanguage(String languageCode) { try (InputStream stream = addon.provideLanguage(languageCode)) { if (stream == null) continue; - MeteorLanguage lang = languages.getOrDefault(languageCode, new MeteorLanguage(definition.rightToLeft())); // noinspection unchecked Object2ObjectOpenHashMap map = GSON.fromJson(new InputStreamReader(stream, StandardCharsets.UTF_8), Object2ObjectOpenHashMap.class); - lang.addCustomTranslation(map); - languages.put(languageCode, lang); + languageMap.putAll(map); MeteorClient.LOG.info("Loaded language {} from addon {}", languageCode, addon.name); } catch (IOException e) { MeteorClient.LOG.error("Error loading language {} from addon {}", languageCode, addon.name, e); } } + + if (!languageMap.isEmpty()) { + languages.put(languageCode, new MeteorLanguage(definition.rightToLeft(), languageMap)); + } } public static String translate(String key, Object... args) { @@ -160,7 +163,8 @@ public static double percentLocalised() { // translation. Maybe that will change in the future. if (isEnglish()) return 100; - double currentLangSize = languages.getOrDefault(mc.options.language.toLowerCase(), new MeteorLanguage(false)).translations.size(); + MeteorLanguage currentLang = languages.get(mc.options.language.toLowerCase()); + double currentLangSize = currentLang != null ? currentLang.translations.size() : 0; return (currentLangSize / getDefaultLanguage().translations.size()) * 100; } @@ -170,44 +174,21 @@ public static boolean isEnglish() { public static class MeteorLanguage extends Language { private final Map translations = new Object2ObjectOpenHashMap<>(); - private final List> customTranslations = new ObjectArrayList<>(); private final boolean rightToLeft; - public MeteorLanguage(boolean rightToLeft) { - this.rightToLeft = rightToLeft; - } - public MeteorLanguage(boolean rightToLeft, Map translations) { - this(rightToLeft); + this.rightToLeft = rightToLeft; this.translations.putAll(translations); } - public void addCustomTranslation(Map customTranslation) { - if (customTranslations.contains(customTranslation)) return; - - customTranslations.add(customTranslation); - } - @Override public String get(String key, String fallback) { - if (translations.containsKey(key)) return translations.get(key); - - for (Map customTranslation : customTranslations) { - if (customTranslation.containsKey(key)) return customTranslation.get(key); - } - - return fallback; + return translations.getOrDefault(key, fallback); } @Override public boolean hasTranslation(String key) { - if (translations.containsKey(key)) return true; - - for (Map customTranslation : customTranslations) { - if (customTranslation.containsKey(key)) return true; - } - - return false; + return translations.containsKey(key); } @Override From d0539e2da2b5e1e00ead90dd4eb0c9695c252a81 Mon Sep 17 00:00:00 2001 From: RacoonDog <32882447+racoondog@users.noreply.github.com> Date: Tue, 18 Nov 2025 01:13:43 -0500 Subject: [PATCH 15/21] these are actually case sensitive --- .../meteorclient/utils/misc/MeteorTranslations.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java b/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java index 6c1f314099..5d121fb62c 100644 --- a/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java +++ b/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java @@ -47,7 +47,7 @@ public static void preInit() { List toLoad = new ArrayList<>(2); toLoad.add(EN_US_CODE); - if (!mc.options.language.equalsIgnoreCase(EN_US_CODE)) toLoad.add(mc.options.language); + if (!mc.options.language.equals(EN_US_CODE)) toLoad.add(mc.options.language); for (String language : toLoad) { loadLanguage(language); @@ -71,7 +71,6 @@ private static boolean doesLangFileExist(MeteorAddon addon, String languageCode) } public static void loadLanguage(String languageCode) { - languageCode = languageCode.toLowerCase(); if (languages.containsKey(languageCode)) return; LanguageDefinition definition = languageDefinitions.get(languageCode); @@ -148,7 +147,7 @@ public static MeteorLanguage getLanguage(String lang) { } public static MeteorLanguage getCurrentLanguage() { - return languages.getOrDefault(mc.options.language.toLowerCase(), getDefaultLanguage()); + return languages.getOrDefault(mc.options.language, getDefaultLanguage()); } public static MeteorLanguage getDefaultLanguage() { @@ -163,13 +162,13 @@ public static double percentLocalised() { // translation. Maybe that will change in the future. if (isEnglish()) return 100; - MeteorLanguage currentLang = languages.get(mc.options.language.toLowerCase()); + MeteorLanguage currentLang = languages.get(mc.options.language); double currentLangSize = currentLang != null ? currentLang.translations.size() : 0; return (currentLangSize / getDefaultLanguage().translations.size()) * 100; } public static boolean isEnglish() { - return mc.options.language.toLowerCase().startsWith("en"); + return mc.options.language.startsWith("en"); } public static class MeteorLanguage extends Language { From 02e834acb763470e0df62cfac37d3dddbadbf3bf Mon Sep 17 00:00:00 2001 From: RacoonDog <32882447+racoondog@users.noreply.github.com> Date: Tue, 18 Nov 2025 01:15:47 -0500 Subject: [PATCH 16/21] automatic addon language registration --- .../meteorclient/addons/MeteorAddon.java | 24 ------------------- .../utils/misc/MeteorTranslations.java | 2 +- 2 files changed, 1 insertion(+), 25 deletions(-) diff --git a/src/main/java/meteordevelopment/meteorclient/addons/MeteorAddon.java b/src/main/java/meteordevelopment/meteorclient/addons/MeteorAddon.java index 01286ead0f..5c2eb55fff 100644 --- a/src/main/java/meteordevelopment/meteorclient/addons/MeteorAddon.java +++ b/src/main/java/meteordevelopment/meteorclient/addons/MeteorAddon.java @@ -7,8 +7,6 @@ import meteordevelopment.meteorclient.utils.render.color.Color; -import java.io.InputStream; - public abstract class MeteorAddon { /** This field is automatically assigned from fabric.mod.json file. * @since 1.21.11 */ // todo replace with exact version when released @@ -40,26 +38,4 @@ public GithubRepo getRepo() { public String getCommit() { return null; } - - /** - * Example implementation: - *
{@code
-     *  @Override
-     *  public InputStream provideLanguage(String lang) {
-     *      return Addon.class.getResourceAsStream("/assets/addon-name/language/" + lang + ".json")
-     *  }
-     * }
-     * 

- * - * Addons should not store their language files in the /assets/xxx/lang/ path as it opens up users to detection - * by servers via the translation exploit. - * Storing them anywhere else should prevent them from getting picked up via the vanilla resource loader. - * - * @param lang A language code in lowercase - * @return An InputStream for the relevant json translation file, or null if the addon doesn't have - * a file for that language. - */ - public InputStream provideLanguage(String lang) { - return null; - } } diff --git a/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java b/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java index 5d121fb62c..92a97a3f16 100644 --- a/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java +++ b/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java @@ -98,7 +98,7 @@ public static void loadLanguage(String languageCode) { for (MeteorAddon addon : AddonManager.ADDONS) { if (addon == MeteorClient.ADDON) continue; - try (InputStream stream = addon.provideLanguage(languageCode)) { + try (InputStream stream = addon.getClass().getResourceAsStream("/assets/" + addon.id + "/language/" + languageCode + ".json")) { if (stream == null) continue; // noinspection unchecked From 241cbe8ac988cdfebd50cb6d1c8e4b78bacb902f Mon Sep 17 00:00:00 2001 From: RacoonDog <32882447+racoondog@users.noreply.github.com> Date: Tue, 18 Nov 2025 01:37:26 -0500 Subject: [PATCH 17/21] oops turns out you dont have those during `@PreInit` --- .../meteorclient/utils/misc/MeteorTranslations.java | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java b/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java index 92a97a3f16..e6b460be07 100644 --- a/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java +++ b/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java @@ -34,17 +34,10 @@ public class MeteorTranslations { private static final String EN_US_CODE = "en_us"; private static final Gson GSON = new Gson(); private static final Map languages = new Object2ObjectOpenHashMap<>(); - private static final Map languageDefinitions = new Object2ObjectOpenHashMap<>(); private static MeteorLanguage defaultLanguage; @PreInit public static void preInit() { - MinecraftClient.getInstance().getLanguageManager().getAllLanguages().forEach((code, definition) -> { - if (hasLocalization(code)) { - languageDefinitions.put(code, definition); - } - }); - List toLoad = new ArrayList<>(2); toLoad.add(EN_US_CODE); if (!mc.options.language.equals(EN_US_CODE)) toLoad.add(mc.options.language); @@ -73,7 +66,7 @@ private static boolean doesLangFileExist(MeteorAddon addon, String languageCode) public static void loadLanguage(String languageCode) { if (languages.containsKey(languageCode)) return; - LanguageDefinition definition = languageDefinitions.get(languageCode); + LanguageDefinition definition = MinecraftClient.getInstance().getLanguageManager().getLanguage(languageCode); if (definition == null) return; Object2ObjectOpenHashMap languageMap = new Object2ObjectOpenHashMap<>(); @@ -138,10 +131,6 @@ public static String translate(String key, String fallback, Object... args) { } } - public static Map getLanguageDefinitions() { - return languageDefinitions; - } - public static MeteorLanguage getLanguage(String lang) { return languages.get(lang); } From eb9f969031d2c4452643bf7f20be3b788535effb Mon Sep 17 00:00:00 2001 From: RacoonDog <32882447+racoondog@users.noreply.github.com> Date: Tue, 18 Nov 2025 01:55:00 -0500 Subject: [PATCH 18/21] fix command descriptions --- .../meteorclient/commands/commands/CommandsCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/meteordevelopment/meteorclient/commands/commands/CommandsCommand.java b/src/main/java/meteordevelopment/meteorclient/commands/commands/CommandsCommand.java index ca08d56e89..7f63c4cd26 100644 --- a/src/main/java/meteordevelopment/meteorclient/commands/commands/CommandsCommand.java +++ b/src/main/java/meteordevelopment/meteorclient/commands/commands/CommandsCommand.java @@ -53,7 +53,7 @@ private MutableText getCommandText(Command command) { } tooltip.append(aliases.formatted(Formatting.GRAY)).append("\n\n"); - tooltip.append(translatable("description")).formatted(Formatting.WHITE); + tooltip.append(command.translatable("description")).formatted(Formatting.WHITE); // Text MutableText text = Text.literal(Utils.nameToTitle(command.getName())); From 9f5fc57143c1328da2b40bd4268b99ea82a1b07e Mon Sep 17 00:00:00 2001 From: RacoonDog <32882447+racoondog@users.noreply.github.com> Date: Tue, 18 Nov 2025 01:55:16 -0500 Subject: [PATCH 19/21] sadly cant use these --- .../utils/misc/MeteorTranslations.java | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java b/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java index e6b460be07..8ca54ff676 100644 --- a/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java +++ b/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java @@ -49,20 +49,6 @@ public static void preInit() { defaultLanguage = getLanguage(EN_US_CODE); } - private static boolean hasLocalization(String languageCode) { - if (doesLangFileExist(MeteorClient.ADDON, languageCode)) return true; - - for (MeteorAddon addon : AddonManager.ADDONS) { - if (doesLangFileExist(addon, languageCode)) return true; - } - - return false; - } - - private static boolean doesLangFileExist(MeteorAddon addon, String languageCode) { - return addon.getClass().getResource("/assets/" + addon.id + "/language/" + languageCode + ".json") != null; - } - public static void loadLanguage(String languageCode) { if (languages.containsKey(languageCode)) return; From a94a64b688491c0746e12579b22f5d59f0c49de8 Mon Sep 17 00:00:00 2001 From: RacoonDog <32882447+racoondog@users.noreply.github.com> Date: Tue, 18 Nov 2025 02:30:25 -0500 Subject: [PATCH 20/21] dont have like all of the languages --- .../meteorclient/mixin/LanguageManagerMixin.java | 1 + .../meteorclient/utils/misc/MeteorTranslations.java | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/java/meteordevelopment/meteorclient/mixin/LanguageManagerMixin.java b/src/main/java/meteordevelopment/meteorclient/mixin/LanguageManagerMixin.java index 228143cef8..4a1868909c 100644 --- a/src/main/java/meteordevelopment/meteorclient/mixin/LanguageManagerMixin.java +++ b/src/main/java/meteordevelopment/meteorclient/mixin/LanguageManagerMixin.java @@ -17,5 +17,6 @@ public class LanguageManagerMixin { @Inject(method = "setLanguage", at = @At("TAIL")) private void onSetLanguage(String languageCode, CallbackInfo ci) { MeteorTranslations.loadLanguage(languageCode); + MeteorTranslations.clearUnusedLanguages(languageCode); } } diff --git a/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java b/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java index 8ca54ff676..c726bb5d4e 100644 --- a/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java +++ b/src/main/java/meteordevelopment/meteorclient/utils/misc/MeteorTranslations.java @@ -22,10 +22,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.IllegalFormatException; -import java.util.List; -import java.util.Map; +import java.util.*; import static meteordevelopment.meteorclient.MeteorClient.mc; @@ -95,6 +92,10 @@ public static void loadLanguage(String languageCode) { } } + public static void clearUnusedLanguages(String currentLanguageCode) { + languages.keySet().removeIf(languageCode -> !languageCode.equals(EN_US_CODE) && !languageCode.equals(currentLanguageCode)); + } + public static String translate(String key, Object... args) { MeteorLanguage currentLang = getCurrentLanguage(); String translated = currentLang.get(key, getDefaultLanguage().get(key)); From 9db28d9cc583bbe68d9c84ceb4888c96a1facdea Mon Sep 17 00:00:00 2001 From: RacoonDog <32882447+racoondog@users.noreply.github.com> Date: Tue, 18 Nov 2025 03:25:55 -0500 Subject: [PATCH 21/21] fix wasp target info --- src/main/resources/assets/meteor-client/language/en_us.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/assets/meteor-client/language/en_us.json b/src/main/resources/assets/meteor-client/language/en_us.json index 35aa0b8e0c..c27502e5fe 100644 --- a/src/main/resources/assets/meteor-client/language/en_us.json +++ b/src/main/resources/assets/meteor-client/language/en_us.json @@ -69,6 +69,6 @@ "meteor.command.vclip.description": "Lets you clip through blocks vertically.", "meteor.command.wasp.description": "Sets the auto wasp target.", "meteor.command.wasp.exception.cant_wasp_self": "You cannot target yourself!", - "meteor.command.wasp.info.target": "%d set as target.", + "meteor.command.wasp.info.target": "%s set as target.", "meteor.command.waypoint.description": "Manages waypoints." }