Commit 4d3f2740 authored by Donlon's avatar Donlon Committed by skylot

Language switch supported

parent f9e7a29c
...@@ -2,6 +2,8 @@ package jadx.gui; ...@@ -2,6 +2,8 @@ package jadx.gui;
import javax.swing.*; import javax.swing.*;
import jadx.gui.utils.LangLocale;
import jadx.gui.utils.NLS;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -10,6 +12,10 @@ import jadx.gui.settings.JadxSettingsAdapter; ...@@ -10,6 +12,10 @@ import jadx.gui.settings.JadxSettingsAdapter;
import jadx.gui.ui.MainWindow; import jadx.gui.ui.MainWindow;
import jadx.gui.utils.logs.LogCollector; import jadx.gui.utils.logs.LogCollector;
import java.util.Locale;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;
public class JadxGUI { public class JadxGUI {
private static final Logger LOG = LoggerFactory.getLogger(JadxGUI.class); private static final Logger LOG = LoggerFactory.getLogger(JadxGUI.class);
...@@ -24,6 +30,7 @@ public class JadxGUI { ...@@ -24,6 +30,7 @@ public class JadxGUI {
if (!tryDefaultLookAndFeel()) { if (!tryDefaultLookAndFeel()) {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} }
NLS.setLocale(settings.getLangLocale());
SwingUtilities.invokeLater(new MainWindow(settings)::open); SwingUtilities.invokeLater(new MainWindow(settings)::open);
} catch (Exception e) { } catch (Exception e) {
LOG.error("Error: {}", e.getMessage(), e); LOG.error("Error: {}", e.getMessage(), e);
......
...@@ -9,6 +9,8 @@ import java.util.List; ...@@ -9,6 +9,8 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import jadx.gui.utils.LangLocale;
import jadx.gui.utils.NLS;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -24,7 +26,7 @@ public class JadxSettings extends JadxCLIArgs { ...@@ -24,7 +26,7 @@ public class JadxSettings extends JadxCLIArgs {
private static final String USER_HOME = System.getProperty("user.home"); private static final String USER_HOME = System.getProperty("user.home");
private static final int RECENT_FILES_COUNT = 15; private static final int RECENT_FILES_COUNT = 15;
private static final int CURRENT_SETTINGS_VERSION = 3; private static final int CURRENT_SETTINGS_VERSION = 4;
private static final Font DEFAULT_FONT = FONT_HACK != null ? FONT_HACK : new RSyntaxTextArea().getFont(); private static final Font DEFAULT_FONT = FONT_HACK != null ? FONT_HACK : new RSyntaxTextArea().getFont();
...@@ -38,6 +40,7 @@ public class JadxSettings extends JadxCLIArgs { ...@@ -38,6 +40,7 @@ public class JadxSettings extends JadxCLIArgs {
private List<String> recentFiles = new ArrayList<>(); private List<String> recentFiles = new ArrayList<>();
private String fontStr = ""; private String fontStr = "";
private String editorThemePath = ""; private String editorThemePath = "";
private LangLocale langLocale = NLS.defaultLocale();
private boolean autoStartJobs = false; private boolean autoStartJobs = false;
private int settingsVersion = 0; private int settingsVersion = 0;
...@@ -149,6 +152,14 @@ public class JadxSettings extends JadxCLIArgs { ...@@ -149,6 +152,14 @@ public class JadxSettings extends JadxCLIArgs {
this.showInconsistentCode = showInconsistentCode; this.showInconsistentCode = showInconsistentCode;
} }
public LangLocale getLangLocale(){
return this.langLocale;
}
public void setLangLocale(LangLocale langLocale) {
this.langLocale = langLocale;
}
public void setCfgOutput(boolean cfgOutput) { public void setCfgOutput(boolean cfgOutput) {
this.cfgOutput = cfgOutput; this.cfgOutput = cfgOutput;
} }
...@@ -253,6 +264,10 @@ public class JadxSettings extends JadxCLIArgs { ...@@ -253,6 +264,10 @@ public class JadxSettings extends JadxCLIArgs {
if (getDeobfuscationMinLength() == 4) { if (getDeobfuscationMinLength() == 4) {
setDeobfuscationMinLength(3); setDeobfuscationMinLength(3);
} }
fromVersion++;
}
if (fromVersion == 3) {
setLangLocale(NLS.defaultLocale());
} }
settingsVersion = CURRENT_SETTINGS_VERSION; settingsVersion = CURRENT_SETTINGS_VERSION;
sync(); sync();
......
...@@ -48,8 +48,8 @@ public class JadxSettingsAdapter { ...@@ -48,8 +48,8 @@ public class JadxSettingsAdapter {
if (settings == null) { if (settings == null) {
return new JadxSettings(); return new JadxSettings();
} }
LOG.debug("Loaded settings: {}", makeString(settings));
settings.fixOnLoad(); settings.fixOnLoad();
LOG.debug("Loaded settings: {}", makeString(settings));
return settings; return settings;
} catch (Exception e) { } catch (Exception e) {
LOG.error("Error load settings", e); LOG.error("Error load settings", e);
...@@ -81,11 +81,8 @@ public class JadxSettingsAdapter { ...@@ -81,11 +81,8 @@ public class JadxSettingsAdapter {
} }
private static <T> void populate(GsonBuilder builder, String json, Class<T> type, final T into) { private static <T> void populate(GsonBuilder builder, String json, Class<T> type, final T into) {
builder.registerTypeAdapter(type, new InstanceCreator<T>() { builder.registerTypeAdapter(type, (InstanceCreator<T>) t -> into)
@Override .create()
public T createInstance(Type t) { .fromJson(json, type);
return into;
}
}).create().fromJson(json, type);
} }
} }
...@@ -55,7 +55,7 @@ class AboutDialog extends JDialog { ...@@ -55,7 +55,7 @@ class AboutDialog extends JDialog {
setModalityType(ModalityType.APPLICATION_MODAL); setModalityType(ModalityType.APPLICATION_MODAL);
setTitle("About JADX"); setTitle(NLS.str("about_dialog.title"));
pack(); pack();
setDefaultCloseOperation(DISPOSE_ON_CLOSE); setDefaultCloseOperation(DISPOSE_ON_CLOSE);
setLocationRelativeTo(null); setLocationRelativeTo(null);
......
...@@ -301,7 +301,10 @@ public abstract class CommonSearchDialog extends JDialog { ...@@ -301,7 +301,10 @@ public abstract class CommonSearchDialog extends JDialog {
protected static class ResultsModel extends AbstractTableModel { protected static class ResultsModel extends AbstractTableModel {
private static final long serialVersionUID = -7821286846923903208L; private static final long serialVersionUID = -7821286846923903208L;
private static final String[] COLUMN_NAMES = {"Node", "Code"}; private static final String[] COLUMN_NAMES = {
NLS.str("search_dialog.col_node"),
NLS.str("search_dialog.col_code")
};
private final transient ArrayList<JNode> rows = new ArrayList<>(); private final transient ArrayList<JNode> rows = new ArrayList<>();
private final transient ResultsTableCellRenderer renderer; private final transient ResultsTableCellRenderer renderer;
...@@ -525,7 +528,7 @@ public abstract class CommonSearchDialog extends JDialog { ...@@ -525,7 +528,7 @@ public abstract class CommonSearchDialog extends JDialog {
TextSearchIndex textIndex = cache.getTextIndex(); TextSearchIndex textIndex = cache.getTextIndex();
if (textIndex == null) { if (textIndex == null) {
warnLabel.setText("Index not initialized, search will be disabled!"); warnLabel.setText(NLS.str("msg.index_not_initialized"));
warnLabel.setVisible(true); warnLabel.setVisible(true);
} }
} }
......
...@@ -40,7 +40,7 @@ class LogViewer extends JDialog { ...@@ -40,7 +40,7 @@ class LogViewer extends JDialog {
level = LEVEL_ITEMS[i]; level = LEVEL_ITEMS[i];
registerLogListener(); registerLogListener();
}); });
JLabel levelLabel = new JLabel(NLS.str("log.level")); JLabel levelLabel = new JLabel(NLS.str("log_viewer.log_level"));
levelLabel.setLabelFor(cb); levelLabel.setLabelFor(cb);
controlPane.add(levelLabel); controlPane.add(levelLabel);
controlPane.add(cb); controlPane.add(cb);
...@@ -56,7 +56,7 @@ class LogViewer extends JDialog { ...@@ -56,7 +56,7 @@ class LogViewer extends JDialog {
contentPane.add(scrollPane, BorderLayout.CENTER); contentPane.add(scrollPane, BorderLayout.CENTER);
contentPane.add(close, BorderLayout.PAGE_END); contentPane.add(close, BorderLayout.PAGE_END);
setTitle("Log Viewer"); setTitle(NLS.str("log_viewer.title"));
pack(); pack();
setSize(800, 600); setSize(800, 600);
setDefaultCloseOperation(DISPOSE_ON_CLOSE); setDefaultCloseOperation(DISPOSE_ON_CLOSE);
......
...@@ -154,7 +154,7 @@ public class MainWindow extends JFrame { ...@@ -154,7 +154,7 @@ public class MainWindow extends JFrame {
String[] exts = {"apk", "dex", "jar", "class", "zip", "aar", "arsc"}; String[] exts = {"apk", "dex", "jar", "class", "zip", "aar", "arsc"};
String description = "supported files: " + Arrays.toString(exts).replace('[', '(').replace(']', ')'); String description = "supported files: " + Arrays.toString(exts).replace('[', '(').replace(']', ')');
fileChooser.setFileFilter(new FileNameExtensionFilter(description, exts)); fileChooser.setFileFilter(new FileNameExtensionFilter(description, exts));
fileChooser.setToolTipText(NLS.str("file.open")); fileChooser.setToolTipText(NLS.str("file.open_action"));
String currentDirectory = settings.getLastOpenFilePath(); String currentDirectory = settings.getLastOpenFilePath();
if (!currentDirectory.isEmpty()) { if (!currentDirectory.isEmpty()) {
fileChooser.setCurrentDirectory(new File(currentDirectory)); fileChooser.setCurrentDirectory(new File(currentDirectory));
...@@ -409,7 +409,7 @@ public class MainWindow extends JFrame { ...@@ -409,7 +409,7 @@ public class MainWindow extends JFrame {
clsSearchAction.putValue(Action.SHORT_DESCRIPTION, NLS.str("menu.class_search")); clsSearchAction.putValue(Action.SHORT_DESCRIPTION, NLS.str("menu.class_search"));
clsSearchAction.putValue(Action.ACCELERATOR_KEY, getKeyStroke(KeyEvent.VK_N, KeyEvent.CTRL_DOWN_MASK)); clsSearchAction.putValue(Action.ACCELERATOR_KEY, getKeyStroke(KeyEvent.VK_N, KeyEvent.CTRL_DOWN_MASK));
Action deobfAction = new AbstractAction(NLS.str("preferences.deobfuscation"), ICON_DEOBF) { Action deobfAction = new AbstractAction(NLS.str("menu.deobfuscation"), ICON_DEOBF) {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
toggleDeobfuscation(); toggleDeobfuscation();
......
package jadx.gui.utils;
import java.util.Locale;
public class LangLocale {
private Locale locale;
public LangLocale(Locale locale) {
this.locale = locale;
}
public LangLocale(String l, String c) {
this.locale = new Locale(l, c);
}
public Locale get() {
return locale;
}
@Override
public String toString() {
return NLS.str("language.name", this);
}
@Override
public boolean equals(Object obj) {
return obj instanceof LangLocale && locale.equals(((LangLocale) obj).get());
}
@Override
public int hashCode() {
return locale.hashCode();
}
}
package jadx.gui.utils; package jadx.gui.utils;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle; import java.util.ResourceBundle;
import java.util.Vector;
public class NLS { public class NLS {
private static Vector<LangLocale> i18nLocales = new Vector<>();
private static ResourceBundle messages; private static Map<LangLocale, Map<String, String>> i18nMessagesMap;
// Use these two fields to avoid invoking Map.get() method twice.
private static Map<String, String> localizedMessagesMap;
private static Map<String, String> fallbackMessagesMap;
private static LangLocale currentLocale;
private static LangLocale localLocale;
private static Charset javaCharset;
private static Charset utf8Charset;
static { static {
load(new Locale("en", "US")); javaCharset = Charset.forName("ISO-8859-1");
utf8Charset = Charset.forName("UTF-8");
i18nMessagesMap = new HashMap<>();
localLocale = new LangLocale(Locale.getDefault());
i18nLocales.add(new LangLocale("en", "US")); // As default language
i18nLocales.add(new LangLocale("zh", "CN"));
i18nLocales.forEach(NLS::load);
fallbackMessagesMap = i18nMessagesMap.get(i18nLocales.get(0));
localizedMessagesMap = i18nMessagesMap.get(i18nLocales.get(0));
} }
private NLS() { private NLS() {
} }
private static void load(Locale locale) { private static void load(LangLocale locale) {
messages = ResourceBundle.getBundle("i18n/Messages", locale); ResourceBundle bundle = ResourceBundle.getBundle("i18n/Messages", locale.get());
Map<String, String> resMap = new HashMap<>();
for(String key : bundle.keySet()){
resMap.put(key, new String(
bundle.getString(key).getBytes(javaCharset),
utf8Charset));
}
i18nMessagesMap.put(locale, resMap);
} }
public static String str(String key) { public static String str(String key) {
return messages.getString(key); if(localizedMessagesMap.containsKey(key)){
return localizedMessagesMap.get(key);
}
return fallbackMessagesMap.get(key);// definitely exists
}
public static String str(String key, LangLocale locale) {
if(i18nMessagesMap.get(locale).containsKey(key)){
return i18nMessagesMap.get(locale).get(key);
}
return fallbackMessagesMap.get(key);// definitely exists
}
public static void setLocale(LangLocale locale) {
if(i18nMessagesMap.containsKey(locale)){
currentLocale = locale;
} else {
currentLocale = i18nLocales.get(0);
}
localizedMessagesMap = i18nMessagesMap.get(currentLocale);
}
public static Vector<LangLocale> getI18nLocales(){
return i18nLocales;
}
public static LangLocale currentLocale() {
return currentLocale;
}
public static LangLocale defaultLocale(){
if(i18nMessagesMap.containsKey(localLocale)){
return localLocale;
} else {
// fallback to english if unsupported
return i18nLocales.get(0);
}
} }
} }
language.name=English
menu.file=File menu.file=File
menu.view=View menu.view=View
menu.recent_files=Recent Files menu.recent_files=Recent Files
...@@ -9,14 +11,14 @@ menu.navigation=Navigation ...@@ -9,14 +11,14 @@ menu.navigation=Navigation
menu.text_search=Text search menu.text_search=Text search
menu.class_search=Class search menu.class_search=Class search
menu.tools=Tools menu.tools=Tools
menu.deobfuscation=Deobfuscation
menu.log=Log Viewer menu.log=Log Viewer
menu.help=Help menu.help=Help
menu.about=About menu.about=About
menu.update_label=New version %s available! menu.update_label=New version %s available!
file.open=Open file file.open_action=Open file...
file.open_title=Open file file.open_title=Open file
file.open_action=Open file
file.save_all=Save all file.save_all=Save all
file.export_gradle=Save as gradle project file.export_gradle=Save as gradle project
file.save_all_msg=Select directory for save decompiled sources file.save_all_msg=Select directory for save decompiled sources
...@@ -45,26 +47,34 @@ nav.forward=Forward ...@@ -45,26 +47,34 @@ nav.forward=Forward
search_dialog.open=Open search_dialog.open=Open
search_dialog.cancel=Cancel search_dialog.cancel=Cancel
search_dialog.open_by_name=Search for text\: search_dialog.open_by_name=Search for text:
search_dialog.search_in=Search definitions of \: search_dialog.search_in=Search definitions of:
search_dialog.class=Class search_dialog.class=Class
search_dialog.method=Method search_dialog.method=Method
search_dialog.field=Field search_dialog.field=Field
search_dialog.code=Code search_dialog.code=Code
search_dialog.options=Search options \: search_dialog.options=Search options:
search_dialog.ignorecase=Case insensitive search_dialog.ignorecase=Case insensitive
search_dialog.next_page=Show next page search_dialog.next_page=Show next page
search_dialog.prev_page=Show previous page search_dialog.prev_page=Show previous page
search_dialog.info_label=Showing results %d to %d of %d search_dialog.info_label=Showing results %1$d to %2$d of %3$d
search_dialog.col_node=Node
search_dialog.col_code=Code
usage_dialog.title=Usage search usage_dialog.title=Usage search
usage_dialog.label=Usage for: usage_dialog.label=Usage for:
log_viewer.title=Log Viewer
log_viewer.log_level=Log level:
about_dialog.title=About JADX
preferences.title=Preferences preferences.title=Preferences
preferences.deobfuscation=Deobfuscation preferences.deobfuscation=Deobfuscation
preferences.editor=Editor preferences.editor=Editor
preferences.decompile=Decompilation preferences.decompile=Decompilation
preferences.other=Other preferences.other=Other
preferences.language=Language
preferences.check_for_updates=Check for updates on startup preferences.check_for_updates=Check for updates on startup
preferences.fallback=Fallback mode (simple dump) preferences.fallback=Fallback mode (simple dump)
preferences.showInconsistentCode=Show inconsistent code preferences.showInconsistentCode=Show inconsistent code
...@@ -90,9 +100,10 @@ preferences.reset_message=Reset settings to default values? ...@@ -90,9 +100,10 @@ preferences.reset_message=Reset settings to default values?
preferences.reset_title=Reset settings preferences.reset_title=Reset settings
msg.open_file=Please open file msg.open_file=Please open file
msg.saving_sources=Saving sources msg.saving_sources=Saving sources...
msg.language_changed_title=Language changed
log.level=Log level: msg.language_changed=New language will be displayed the next time application starts.
msg.index_not_initialized=Index not initialized, search will be disabled!
popup.undo=Undo popup.undo=Undo
popup.redo=Redo popup.redo=Redo
......
language.name=中文(简体)
menu.file=文件 menu.file=文件
menu.view=视图 menu.view=视图
menu.recent_files=最近打开文件 menu.recent_files=最近打开文件
...@@ -82,6 +84,8 @@ preferences.reset_title=重置设置 ...@@ -82,6 +84,8 @@ preferences.reset_title=重置设置
msg.open_file=请打开文件 msg.open_file=请打开文件
msg.saving_sources=保存资源 msg.saving_sources=保存资源
log.level=日志等级: log.level=日志等级:
msg.language_changed_title=语言已更改
msg.language_changed=在下次启动时将会显示新的语言。
popup.undo=撤销 popup.undo=撤销
popup.redo=重复上一次操作 popup.redo=重复上一次操作
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment