diff options
| author | 2025-08-19 16:46:19 +0100 | |
|---|---|---|
| committer | 2025-09-13 09:14:23 +0100 | |
| commit | 40ab19aee2ae2a26dee37184f79596017e48edf7 (patch) | |
| tree | 18af87109c167a92a9579908403be0bf56b39f84 | |
| parent | Fix guava again (#562) (diff) | |
| download | enigma-40ab19aee2ae2a26dee37184f79596017e48edf7.tar.gz enigma-40ab19aee2ae2a26dee37184f79596017e48edf7.tar.xz enigma-40ab19aee2ae2a26dee37184f79596017e48edf7.zip | |
Add I18n service
9 files changed, 69 insertions, 26 deletions
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; | |||
| 44 | import org.jetbrains.annotations.Nullable; | 44 | import org.jetbrains.annotations.Nullable; |
| 45 | 45 | ||
| 46 | import cuchaz.enigma.Enigma; | 46 | import cuchaz.enigma.Enigma; |
| 47 | import cuchaz.enigma.EnigmaProfile; | ||
| 48 | import cuchaz.enigma.analysis.EntryReference; | 47 | import cuchaz.enigma.analysis.EntryReference; |
| 49 | import cuchaz.enigma.gui.config.Themes; | 48 | import cuchaz.enigma.gui.config.Themes; |
| 50 | import cuchaz.enigma.gui.config.UiConfig; | 49 | import cuchaz.enigma.gui.config.UiConfig; |
| @@ -124,9 +123,9 @@ public class Gui { | |||
| 124 | public final JFileChooser exportJarFileChooser = new JFileChooser(); | 123 | public final JFileChooser exportJarFileChooser = new JFileChooser(); |
| 125 | public SearchDialog searchDialog; | 124 | public SearchDialog searchDialog; |
| 126 | 125 | ||
| 127 | public Gui(EnigmaProfile profile, Set<EditableType> editableTypes) { | 126 | public Gui(Enigma enigma, Set<EditableType> editableTypes) { |
| 128 | this.editableTypes = editableTypes; | 127 | this.editableTypes = editableTypes; |
| 129 | this.controller = new GuiController(this, profile); | 128 | this.controller = new GuiController(this, enigma); |
| 130 | this.structurePanel = new StructurePanel(this); | 129 | this.structurePanel = new StructurePanel(this); |
| 131 | this.deobfPanel = new DeobfPanel(this); | 130 | this.deobfPanel = new DeobfPanel(this); |
| 132 | this.infoPanel = new IdentifierPanel(this); | 131 | 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; | |||
| 33 | import org.jetbrains.annotations.ApiStatus; | 33 | import org.jetbrains.annotations.ApiStatus; |
| 34 | 34 | ||
| 35 | import cuchaz.enigma.Enigma; | 35 | import cuchaz.enigma.Enigma; |
| 36 | import cuchaz.enigma.EnigmaProfile; | ||
| 37 | import cuchaz.enigma.EnigmaProject; | 36 | import cuchaz.enigma.EnigmaProject; |
| 38 | import cuchaz.enigma.analysis.ClassImplementationsTreeNode; | 37 | import cuchaz.enigma.analysis.ClassImplementationsTreeNode; |
| 39 | import cuchaz.enigma.analysis.ClassInheritanceTreeNode; | 38 | import cuchaz.enigma.analysis.ClassInheritanceTreeNode; |
| @@ -111,9 +110,9 @@ public class GuiController implements ClientPacketHandler { | |||
| 111 | 110 | ||
| 112 | private History<EntryReference<Entry<?>, Entry<?>>> referenceHistory; | 111 | private History<EntryReference<Entry<?>, Entry<?>>> referenceHistory; |
| 113 | 112 | ||
| 114 | public GuiController(Gui gui, EnigmaProfile profile) { | 113 | public GuiController(Gui gui, Enigma enigma) { |
| 115 | this.gui = gui; | 114 | this.gui = gui; |
| 116 | this.enigma = Enigma.builder().setProfile(profile).build(); | 115 | this.enigma = enigma; |
| 117 | } | 116 | } |
| 118 | 117 | ||
| 119 | public boolean isDirty() { | 118 | 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; | |||
| 27 | import joptsimple.OptionSpec; | 27 | import joptsimple.OptionSpec; |
| 28 | import joptsimple.ValueConverter; | 28 | import joptsimple.ValueConverter; |
| 29 | 29 | ||
| 30 | import cuchaz.enigma.Enigma; | ||
| 30 | import cuchaz.enigma.EnigmaProfile; | 31 | import cuchaz.enigma.EnigmaProfile; |
| 31 | import cuchaz.enigma.gui.config.Themes; | 32 | import cuchaz.enigma.gui.config.Themes; |
| 32 | import cuchaz.enigma.gui.config.UiConfig; | 33 | import cuchaz.enigma.gui.config.UiConfig; |
| @@ -101,8 +102,9 @@ public class Main { | |||
| 101 | } | 102 | } |
| 102 | 103 | ||
| 103 | EnigmaProfile parsedProfile = EnigmaProfile.read(options.valueOf(profile)); | 104 | EnigmaProfile parsedProfile = EnigmaProfile.read(options.valueOf(profile)); |
| 105 | Enigma enigma = Enigma.builder().setProfile(parsedProfile).build(); | ||
| 104 | 106 | ||
| 105 | I18n.setLanguage(UiConfig.getLanguage()); | 107 | I18n.setLanguage(UiConfig.getLanguage(), enigma.getServices()); |
| 106 | 108 | ||
| 107 | // Provide fallback anti-aliasing for desktop environments the JRE doesn't recognize | 109 | // Provide fallback anti-aliasing for desktop environments the JRE doesn't recognize |
| 108 | if (Toolkit.getDefaultToolkit().getDesktopProperty("awt.font.desktophints") == null) { | 110 | if (Toolkit.getDefaultToolkit().getDesktopProperty("awt.font.desktophints") == null) { |
| @@ -117,7 +119,7 @@ public class Main { | |||
| 117 | 119 | ||
| 118 | Themes.setupTheme(); | 120 | Themes.setupTheme(); |
| 119 | 121 | ||
| 120 | Gui gui = new Gui(parsedProfile, editables); | 122 | Gui gui = new Gui(enigma, editables); |
| 121 | GuiController controller = gui.getController(); | 123 | GuiController controller = gui.getController(); |
| 122 | 124 | ||
| 123 | if (options.has("single-class-tree")) { | 125 | 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 { | |||
| 524 | } | 524 | } |
| 525 | } | 525 | } |
| 526 | 526 | ||
| 527 | private static void prepareLanguagesMenu(JMenu languagesMenu) { | 527 | private void prepareLanguagesMenu(JMenu languagesMenu) { |
| 528 | ButtonGroup languageGroup = new ButtonGroup(); | 528 | ButtonGroup languageGroup = new ButtonGroup(); |
| 529 | 529 | ||
| 530 | for (String lang : I18n.getAvailableLanguages()) { | 530 | for (String lang : I18n.getAvailableLanguages()) { |
| @@ -537,7 +537,7 @@ public class MenuBar { | |||
| 537 | 537 | ||
| 538 | languageButton.addActionListener(event -> { | 538 | languageButton.addActionListener(event -> { |
| 539 | UiConfig.setLanguage(lang); | 539 | UiConfig.setLanguage(lang); |
| 540 | I18n.setLanguage(lang); | 540 | I18n.setLanguage(lang, gui.getController().enigma.getServices()); |
| 541 | LanguageUtil.dispatchLanguageChange(); | 541 | LanguageUtil.dispatchLanguageChange(); |
| 542 | UiConfig.save(); | 542 | UiConfig.save(); |
| 543 | }); | 543 | }); |
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; | |||
| 40 | import cuchaz.enigma.classprovider.ClassProvider; | 40 | import cuchaz.enigma.classprovider.ClassProvider; |
| 41 | import cuchaz.enigma.classprovider.CombiningClassProvider; | 41 | import cuchaz.enigma.classprovider.CombiningClassProvider; |
| 42 | import cuchaz.enigma.classprovider.JarClassProvider; | 42 | import cuchaz.enigma.classprovider.JarClassProvider; |
| 43 | import cuchaz.enigma.utils.I18n; | ||
| 43 | import cuchaz.enigma.utils.OrderingImpl; | 44 | import cuchaz.enigma.utils.OrderingImpl; |
| 44 | import cuchaz.enigma.utils.Utils; | 45 | import cuchaz.enigma.utils.Utils; |
| 45 | 46 | ||
| @@ -122,6 +123,9 @@ public class Enigma { | |||
| 122 | .forEach(plugin -> plugin.get().init(pluginContext)); | 123 | .forEach(plugin -> plugin.get().init(pluginContext)); |
| 123 | 124 | ||
| 124 | EnigmaServices services = pluginContext.buildServices(); | 125 | EnigmaServices services = pluginContext.buildServices(); |
| 126 | |||
| 127 | I18n.initialize(services); | ||
| 128 | |||
| 125 | return new Enigma(profile, services); | 129 | return new Enigma(profile, services); |
| 126 | } | 130 | } |
| 127 | } | 131 | } |
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; | |||
| 2 | 2 | ||
| 3 | import cuchaz.enigma.api.EnigmaPlugin; | 3 | import cuchaz.enigma.api.EnigmaPlugin; |
| 4 | import cuchaz.enigma.api.EnigmaPluginContext; | 4 | import cuchaz.enigma.api.EnigmaPluginContext; |
| 5 | import cuchaz.enigma.api.service.I18nService; | ||
| 5 | import cuchaz.enigma.source.DecompilerService; | 6 | import cuchaz.enigma.source.DecompilerService; |
| 6 | import cuchaz.enigma.source.Decompilers; | 7 | import cuchaz.enigma.source.Decompilers; |
| 7 | 8 | ||
| @@ -9,6 +10,8 @@ public final class BuiltinPlugin implements EnigmaPlugin { | |||
| 9 | @Override | 10 | @Override |
| 10 | public void init(EnigmaPluginContext ctx) { | 11 | public void init(EnigmaPluginContext ctx) { |
| 11 | registerDecompilerServices(ctx); | 12 | registerDecompilerServices(ctx); |
| 13 | |||
| 14 | ctx.registerService("enigma:i18n", I18nService.TYPE, () -> language -> BuiltinPlugin.class.getResourceAsStream("/lang/" + language + ".json")); | ||
| 12 | } | 15 | } |
| 13 | 16 | ||
| 14 | private void registerDecompilerServices(EnigmaPluginContext ctx) { | 17 | 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 @@ | |||
| 1 | package cuchaz.enigma.api; | ||
| 2 | |||
| 3 | public final class I18n { | ||
| 4 | private I18n() { | ||
| 5 | } | ||
| 6 | |||
| 7 | public static String translate(String key) { | ||
| 8 | return cuchaz.enigma.utils.I18n.translate(key); | ||
| 9 | } | ||
| 10 | |||
| 11 | public static String translate(String key, Object... args) { | ||
| 12 | return cuchaz.enigma.utils.I18n.translateFormatted(key, args); | ||
| 13 | } | ||
| 14 | } | ||
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 @@ | |||
| 1 | package cuchaz.enigma.api.service; | ||
| 2 | |||
| 3 | import java.io.InputStream; | ||
| 4 | |||
| 5 | import org.jetbrains.annotations.Nullable; | ||
| 6 | |||
| 7 | public interface I18nService extends EnigmaService { | ||
| 8 | EnigmaServiceType<I18nService> TYPE = EnigmaServiceType.create("translation"); | ||
| 9 | |||
| 10 | @Nullable | ||
| 11 | InputStream getTranslationResource(String language); | ||
| 12 | } | ||
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; | |||
| 7 | import java.nio.charset.StandardCharsets; | 7 | import java.nio.charset.StandardCharsets; |
| 8 | import java.util.ArrayList; | 8 | import java.util.ArrayList; |
| 9 | import java.util.Arrays; | 9 | import java.util.Arrays; |
| 10 | import java.util.Collections; | ||
| 11 | import java.util.HashMap; | 10 | import java.util.HashMap; |
| 12 | import java.util.List; | 11 | import java.util.List; |
| 13 | import java.util.Map; | 12 | import java.util.Map; |
| @@ -16,26 +15,37 @@ import java.util.stream.Collectors; | |||
| 16 | 15 | ||
| 17 | import com.google.gson.Gson; | 16 | import com.google.gson.Gson; |
| 18 | 17 | ||
| 18 | import cuchaz.enigma.EnigmaServices; | ||
| 19 | import cuchaz.enigma.api.service.I18nService; | ||
| 20 | |||
| 19 | public class I18n { | 21 | public class I18n { |
| 20 | public static final String DEFAULT_LANGUAGE = "en_us"; | 22 | public static final String DEFAULT_LANGUAGE = "en_us"; |
| 21 | private static final Gson GSON = new Gson(); | 23 | private static final Gson GSON = new Gson(); |
| 22 | private static Map<String, String> defaultTranslations = load(DEFAULT_LANGUAGE); | 24 | private static Map<String, String> defaultTranslations = Map.of(); |
| 23 | private static Map<String, String> translations = defaultTranslations; | 25 | private static Map<String, String> translations = Map.of(); |
| 24 | private static Map<String, String> languageNames = new HashMap<>(); | 26 | private static final Map<String, String> LANGUAGE_NAMES = new HashMap<>(); |
| 27 | |||
| 28 | public static void initialize(EnigmaServices services) { | ||
| 29 | translations = defaultTranslations = load(DEFAULT_LANGUAGE, services); | ||
| 30 | } | ||
| 25 | 31 | ||
| 26 | @SuppressWarnings("unchecked") | 32 | @SuppressWarnings("unchecked") |
| 27 | public static Map<String, String> load(String language) { | 33 | private static Map<String, String> load(String language, EnigmaServices services) { |
| 28 | try (InputStream inputStream = I18n.class.getResourceAsStream("/lang/" + language + ".json")) { | 34 | Map<String, String> translations = new HashMap<>(); |
| 29 | if (inputStream != null) { | 35 | |
| 30 | try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { | 36 | for (I18nService i18nService : services.get(I18nService.TYPE)) { |
| 31 | return GSON.fromJson(reader, Map.class); | 37 | try (InputStream inputStream = i18nService.getTranslationResource(language)) { |
| 38 | if (inputStream != null) { | ||
| 39 | try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { | ||
| 40 | translations.putAll(GSON.fromJson(reader, Map.class)); | ||
| 41 | } | ||
| 32 | } | 42 | } |
| 43 | } catch (IOException e) { | ||
| 44 | e.printStackTrace(); | ||
| 33 | } | 45 | } |
| 34 | } catch (IOException e) { | ||
| 35 | e.printStackTrace(); | ||
| 36 | } | 46 | } |
| 37 | 47 | ||
| 38 | return Collections.emptyMap(); | 48 | return translations; |
| 39 | } | 49 | } |
| 40 | 50 | ||
| 41 | public static String translateOrNull(String key) { | 51 | public static String translateOrNull(String key) { |
| @@ -76,11 +86,11 @@ public class I18n { | |||
| 76 | } | 86 | } |
| 77 | 87 | ||
| 78 | public static String getLanguageName(String language) { | 88 | public static String getLanguageName(String language) { |
| 79 | return languageNames.get(language); | 89 | return LANGUAGE_NAMES.get(language); |
| 80 | } | 90 | } |
| 81 | 91 | ||
| 82 | public static void setLanguage(String language) { | 92 | public static void setLanguage(String language, EnigmaServices services) { |
| 83 | translations = load(language); | 93 | translations = load(language, services); |
| 84 | } | 94 | } |
| 85 | 95 | ||
| 86 | public static ArrayList<String> getAvailableLanguages() { | 96 | public static ArrayList<String> getAvailableLanguages() { |
| @@ -112,7 +122,7 @@ public class I18n { | |||
| 112 | try (InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream("lang/" + fileName + ".json")) { | 122 | try (InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream("lang/" + fileName + ".json")) { |
| 113 | try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8))) { | 123 | try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8))) { |
| 114 | Map<?, ?> map = GSON.fromJson(reader, Map.class); | 124 | Map<?, ?> map = GSON.fromJson(reader, Map.class); |
| 115 | languageNames.put(fileName, map.get("language").toString()); | 125 | LANGUAGE_NAMES.put(fileName, map.get("language").toString()); |
| 116 | } | 126 | } |
| 117 | } catch (IOException e) { | 127 | } catch (IOException e) { |
| 118 | e.printStackTrace(); | 128 | e.printStackTrace(); |