From 40ab19aee2ae2a26dee37184f79596017e48edf7 Mon Sep 17 00:00:00 2001 From: Joe Date: Tue, 19 Aug 2025 16:46:19 +0100 Subject: Add I18n service --- .../src/main/java/cuchaz/enigma/gui/Gui.java | 5 ++- .../main/java/cuchaz/enigma/gui/GuiController.java | 5 ++- .../src/main/java/cuchaz/enigma/gui/Main.java | 6 ++-- .../java/cuchaz/enigma/gui/elements/MenuBar.java | 4 +-- enigma/src/main/java/cuchaz/enigma/Enigma.java | 4 +++ .../java/cuchaz/enigma/analysis/BuiltinPlugin.java | 3 ++ enigma/src/main/java/cuchaz/enigma/api/I18n.java | 14 ++++++++ .../cuchaz/enigma/api/service/I18nService.java | 12 +++++++ enigma/src/main/java/cuchaz/enigma/utils/I18n.java | 42 +++++++++++++--------- 9 files changed, 69 insertions(+), 26 deletions(-) create mode 100644 enigma/src/main/java/cuchaz/enigma/api/I18n.java create mode 100644 enigma/src/main/java/cuchaz/enigma/api/service/I18nService.java diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java index c85c4044..5881900c 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java @@ -44,7 +44,6 @@ import javax.swing.tree.DefaultMutableTreeNode; import org.jetbrains.annotations.Nullable; import cuchaz.enigma.Enigma; -import cuchaz.enigma.EnigmaProfile; import cuchaz.enigma.analysis.EntryReference; import cuchaz.enigma.gui.config.Themes; import cuchaz.enigma.gui.config.UiConfig; @@ -124,9 +123,9 @@ public class Gui { public final JFileChooser exportJarFileChooser = new JFileChooser(); public SearchDialog searchDialog; - public Gui(EnigmaProfile profile, Set editableTypes) { + public Gui(Enigma enigma, Set editableTypes) { this.editableTypes = editableTypes; - this.controller = new GuiController(this, profile); + this.controller = new GuiController(this, enigma); this.structurePanel = new StructurePanel(this); this.deobfPanel = new DeobfPanel(this); this.infoPanel = new IdentifierPanel(this); diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java index e8e4dbde..e1e8521a 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java @@ -33,7 +33,6 @@ import javax.swing.SwingUtilities; import org.jetbrains.annotations.ApiStatus; import cuchaz.enigma.Enigma; -import cuchaz.enigma.EnigmaProfile; import cuchaz.enigma.EnigmaProject; import cuchaz.enigma.analysis.ClassImplementationsTreeNode; import cuchaz.enigma.analysis.ClassInheritanceTreeNode; @@ -111,9 +110,9 @@ public class GuiController implements ClientPacketHandler { private History, Entry>> referenceHistory; - public GuiController(Gui gui, EnigmaProfile profile) { + public GuiController(Gui gui, Enigma enigma) { this.gui = gui; - this.enigma = Enigma.builder().setProfile(profile).build(); + this.enigma = enigma; } public boolean isDirty() { diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/Main.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/Main.java index 6dd89572..66402ad8 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/Main.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/Main.java @@ -27,6 +27,7 @@ import joptsimple.OptionSet; import joptsimple.OptionSpec; import joptsimple.ValueConverter; +import cuchaz.enigma.Enigma; import cuchaz.enigma.EnigmaProfile; import cuchaz.enigma.gui.config.Themes; import cuchaz.enigma.gui.config.UiConfig; @@ -101,8 +102,9 @@ public class Main { } EnigmaProfile parsedProfile = EnigmaProfile.read(options.valueOf(profile)); + Enigma enigma = Enigma.builder().setProfile(parsedProfile).build(); - I18n.setLanguage(UiConfig.getLanguage()); + I18n.setLanguage(UiConfig.getLanguage(), enigma.getServices()); // Provide fallback anti-aliasing for desktop environments the JRE doesn't recognize if (Toolkit.getDefaultToolkit().getDesktopProperty("awt.font.desktophints") == null) { @@ -117,7 +119,7 @@ public class Main { Themes.setupTheme(); - Gui gui = new Gui(parsedProfile, editables); + Gui gui = new Gui(enigma, editables); GuiController controller = gui.getController(); if (options.has("single-class-tree")) { diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java index 78050bb3..1789def1 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java @@ -524,7 +524,7 @@ public class MenuBar { } } - private static void prepareLanguagesMenu(JMenu languagesMenu) { + private void prepareLanguagesMenu(JMenu languagesMenu) { ButtonGroup languageGroup = new ButtonGroup(); for (String lang : I18n.getAvailableLanguages()) { @@ -537,7 +537,7 @@ public class MenuBar { languageButton.addActionListener(event -> { UiConfig.setLanguage(lang); - I18n.setLanguage(lang); + I18n.setLanguage(lang, gui.getController().enigma.getServices()); LanguageUtil.dispatchLanguageChange(); UiConfig.save(); }); diff --git a/enigma/src/main/java/cuchaz/enigma/Enigma.java b/enigma/src/main/java/cuchaz/enigma/Enigma.java index b98d71fd..729a1149 100644 --- a/enigma/src/main/java/cuchaz/enigma/Enigma.java +++ b/enigma/src/main/java/cuchaz/enigma/Enigma.java @@ -40,6 +40,7 @@ import cuchaz.enigma.classprovider.CachingClassProvider; import cuchaz.enigma.classprovider.ClassProvider; import cuchaz.enigma.classprovider.CombiningClassProvider; import cuchaz.enigma.classprovider.JarClassProvider; +import cuchaz.enigma.utils.I18n; import cuchaz.enigma.utils.OrderingImpl; import cuchaz.enigma.utils.Utils; @@ -122,6 +123,9 @@ public class Enigma { .forEach(plugin -> plugin.get().init(pluginContext)); EnigmaServices services = pluginContext.buildServices(); + + I18n.initialize(services); + return new Enigma(profile, services); } } diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/BuiltinPlugin.java b/enigma/src/main/java/cuchaz/enigma/analysis/BuiltinPlugin.java index aed949af..2e68478a 100644 --- a/enigma/src/main/java/cuchaz/enigma/analysis/BuiltinPlugin.java +++ b/enigma/src/main/java/cuchaz/enigma/analysis/BuiltinPlugin.java @@ -2,6 +2,7 @@ package cuchaz.enigma.analysis; import cuchaz.enigma.api.EnigmaPlugin; import cuchaz.enigma.api.EnigmaPluginContext; +import cuchaz.enigma.api.service.I18nService; import cuchaz.enigma.source.DecompilerService; import cuchaz.enigma.source.Decompilers; @@ -9,6 +10,8 @@ public final class BuiltinPlugin implements EnigmaPlugin { @Override public void init(EnigmaPluginContext ctx) { registerDecompilerServices(ctx); + + ctx.registerService("enigma:i18n", I18nService.TYPE, () -> language -> BuiltinPlugin.class.getResourceAsStream("/lang/" + language + ".json")); } private void registerDecompilerServices(EnigmaPluginContext ctx) { diff --git a/enigma/src/main/java/cuchaz/enigma/api/I18n.java b/enigma/src/main/java/cuchaz/enigma/api/I18n.java new file mode 100644 index 00000000..f8252da9 --- /dev/null +++ b/enigma/src/main/java/cuchaz/enigma/api/I18n.java @@ -0,0 +1,14 @@ +package cuchaz.enigma.api; + +public final class I18n { + private I18n() { + } + + public static String translate(String key) { + return cuchaz.enigma.utils.I18n.translate(key); + } + + public static String translate(String key, Object... args) { + return cuchaz.enigma.utils.I18n.translateFormatted(key, args); + } +} diff --git a/enigma/src/main/java/cuchaz/enigma/api/service/I18nService.java b/enigma/src/main/java/cuchaz/enigma/api/service/I18nService.java new file mode 100644 index 00000000..aa59bb27 --- /dev/null +++ b/enigma/src/main/java/cuchaz/enigma/api/service/I18nService.java @@ -0,0 +1,12 @@ +package cuchaz.enigma.api.service; + +import java.io.InputStream; + +import org.jetbrains.annotations.Nullable; + +public interface I18nService extends EnigmaService { + EnigmaServiceType TYPE = EnigmaServiceType.create("translation"); + + @Nullable + InputStream getTranslationResource(String language); +} diff --git a/enigma/src/main/java/cuchaz/enigma/utils/I18n.java b/enigma/src/main/java/cuchaz/enigma/utils/I18n.java index fff73526..4648decc 100644 --- a/enigma/src/main/java/cuchaz/enigma/utils/I18n.java +++ b/enigma/src/main/java/cuchaz/enigma/utils/I18n.java @@ -7,7 +7,6 @@ import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -16,26 +15,37 @@ import java.util.stream.Collectors; import com.google.gson.Gson; +import cuchaz.enigma.EnigmaServices; +import cuchaz.enigma.api.service.I18nService; + public class I18n { public static final String DEFAULT_LANGUAGE = "en_us"; private static final Gson GSON = new Gson(); - private static Map defaultTranslations = load(DEFAULT_LANGUAGE); - private static Map translations = defaultTranslations; - private static Map languageNames = new HashMap<>(); + private static Map defaultTranslations = Map.of(); + private static Map translations = Map.of(); + private static final Map LANGUAGE_NAMES = new HashMap<>(); + + public static void initialize(EnigmaServices services) { + translations = defaultTranslations = load(DEFAULT_LANGUAGE, services); + } @SuppressWarnings("unchecked") - public static Map load(String language) { - try (InputStream inputStream = I18n.class.getResourceAsStream("/lang/" + language + ".json")) { - if (inputStream != null) { - try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { - return GSON.fromJson(reader, Map.class); + private static Map load(String language, EnigmaServices services) { + Map translations = new HashMap<>(); + + for (I18nService i18nService : services.get(I18nService.TYPE)) { + try (InputStream inputStream = i18nService.getTranslationResource(language)) { + if (inputStream != null) { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { + translations.putAll(GSON.fromJson(reader, Map.class)); + } } + } catch (IOException e) { + e.printStackTrace(); } - } catch (IOException e) { - e.printStackTrace(); } - return Collections.emptyMap(); + return translations; } public static String translateOrNull(String key) { @@ -76,11 +86,11 @@ public class I18n { } public static String getLanguageName(String language) { - return languageNames.get(language); + return LANGUAGE_NAMES.get(language); } - public static void setLanguage(String language) { - translations = load(language); + public static void setLanguage(String language, EnigmaServices services) { + translations = load(language, services); } public static ArrayList getAvailableLanguages() { @@ -112,7 +122,7 @@ public class I18n { try (InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream("lang/" + fileName + ".json")) { try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8))) { Map map = GSON.fromJson(reader, Map.class); - languageNames.put(fileName, map.get("language").toString()); + LANGUAGE_NAMES.put(fileName, map.get("language").toString()); } } catch (IOException e) { e.printStackTrace(); -- cgit v1.2.3