提交 4d3f2740 编写于 作者: D Donlon 提交者: skylot

Language switch supported

上级 f9e7a29c
......@@ -2,6 +2,8 @@ package jadx.gui;
import javax.swing.*;
import jadx.gui.utils.LangLocale;
import jadx.gui.utils.NLS;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -10,6 +12,10 @@ import jadx.gui.settings.JadxSettingsAdapter;
import jadx.gui.ui.MainWindow;
import jadx.gui.utils.logs.LogCollector;
import java.util.Locale;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;
public class JadxGUI {
private static final Logger LOG = LoggerFactory.getLogger(JadxGUI.class);
......@@ -24,6 +30,7 @@ public class JadxGUI {
if (!tryDefaultLookAndFeel()) {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
}
NLS.setLocale(settings.getLangLocale());
SwingUtilities.invokeLater(new MainWindow(settings)::open);
} catch (Exception e) {
LOG.error("Error: {}", e.getMessage(), e);
......
......@@ -9,6 +9,8 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import jadx.gui.utils.LangLocale;
import jadx.gui.utils.NLS;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -24,7 +26,7 @@ public class JadxSettings extends JadxCLIArgs {
private static final String USER_HOME = System.getProperty("user.home");
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();
......@@ -38,6 +40,7 @@ public class JadxSettings extends JadxCLIArgs {
private List<String> recentFiles = new ArrayList<>();
private String fontStr = "";
private String editorThemePath = "";
private LangLocale langLocale = NLS.defaultLocale();
private boolean autoStartJobs = false;
private int settingsVersion = 0;
......@@ -149,6 +152,14 @@ public class JadxSettings extends JadxCLIArgs {
this.showInconsistentCode = showInconsistentCode;
}
public LangLocale getLangLocale(){
return this.langLocale;
}
public void setLangLocale(LangLocale langLocale) {
this.langLocale = langLocale;
}
public void setCfgOutput(boolean cfgOutput) {
this.cfgOutput = cfgOutput;
}
......@@ -253,6 +264,10 @@ public class JadxSettings extends JadxCLIArgs {
if (getDeobfuscationMinLength() == 4) {
setDeobfuscationMinLength(3);
}
fromVersion++;
}
if (fromVersion == 3) {
setLangLocale(NLS.defaultLocale());
}
settingsVersion = CURRENT_SETTINGS_VERSION;
sync();
......
......@@ -48,8 +48,8 @@ public class JadxSettingsAdapter {
if (settings == null) {
return new JadxSettings();
}
LOG.debug("Loaded settings: {}", makeString(settings));
settings.fixOnLoad();
LOG.debug("Loaded settings: {}", makeString(settings));
return settings;
} catch (Exception e) {
LOG.error("Error load settings", e);
......@@ -81,11 +81,8 @@ public class JadxSettingsAdapter {
}
private static <T> void populate(GsonBuilder builder, String json, Class<T> type, final T into) {
builder.registerTypeAdapter(type, new InstanceCreator<T>() {
@Override
public T createInstance(Type t) {
return into;
}
}).create().fromJson(json, type);
builder.registerTypeAdapter(type, (InstanceCreator<T>) t -> into)
.create()
.fromJson(json, type);
}
}
package jadx.gui.settings;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Arrays;
import java.util.Collection;
import java.util.stream.Stream;
import jadx.gui.utils.LangLocale;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import say.swing.JFontChooser;
......@@ -34,6 +29,7 @@ public class JadxSettingsWindow extends JDialog {
private final transient MainWindow mainWindow;
private final transient JadxSettings settings;
private final transient String startSettings;
private final transient LangLocale prevLang;
private transient boolean needReload = false;
......@@ -41,16 +37,17 @@ public class JadxSettingsWindow extends JDialog {
this.mainWindow = mainWindow;
this.settings = settings;
this.startSettings = JadxSettingsAdapter.makeString(settings);
this.prevLang = settings.getLangLocale();
initUI();
registerBundledFonts();
setTitle(NLS.str("preferences.title"));
setSize(400, 550);
setLocationRelativeTo(null);
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
setModalityType(ModalityType.APPLICATION_MODAL);
pack();
setLocationRelativeTo(null);
}
public static void registerBundledFonts() {
......@@ -70,39 +67,41 @@ public class JadxSettingsWindow extends JDialog {
panel.add(makeOtherGroup());
JButton saveBtn = new JButton(NLS.str("preferences.save"));
saveBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
settings.sync();
if (needReload) {
mainWindow.reOpenFile();
}
dispose();
saveBtn.addActionListener(event -> {
settings.sync();
if (needReload) {
mainWindow.reOpenFile();
}
if (!settings.getLangLocale().equals(prevLang)){
JOptionPane.showMessageDialog(
this,
NLS.str("msg.language_changed", settings.getLangLocale()),
NLS.str("msg.language_changed_title", settings.getLangLocale()),
JOptionPane.INFORMATION_MESSAGE
);
}
dispose();
});
JButton cancelButton = new JButton(NLS.str("preferences.cancel"));
cancelButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
JadxSettingsAdapter.fill(settings, startSettings);
dispose();
}
cancelButton.addActionListener(event -> {
JadxSettingsAdapter.fill(settings, startSettings);
dispose();
});
JButton resetBtn = new JButton(NLS.str("preferences.reset"));
resetBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
int res = JOptionPane.showConfirmDialog(
JadxSettingsWindow.this,
NLS.str("preferences.reset_message"),
NLS.str("preferences.reset_title"),
JOptionPane.YES_NO_OPTION);
if (res == JOptionPane.YES_OPTION) {
String defaults = JadxSettingsAdapter.makeString(new JadxSettings());
JadxSettingsAdapter.fill(settings, defaults);
getContentPane().removeAll();
initUI();
pack();
repaint();
}
resetBtn.addActionListener(event -> {
int res = JOptionPane.showConfirmDialog(
JadxSettingsWindow.this,
NLS.str("preferences.reset_message"),
NLS.str("preferences.reset_title"),
JOptionPane.YES_NO_OPTION);
if (res == JOptionPane.YES_OPTION) {
String defaults = JadxSettingsAdapter.makeString(new JadxSettings());
JadxSettingsAdapter.fill(settings, defaults);
getContentPane().removeAll();
initUI();
pack();
repaint();
}
});
......@@ -124,49 +123,37 @@ public class JadxSettingsWindow extends JDialog {
private SettingsGroup makeDeobfuscationGroup() {
JCheckBox deobfOn = new JCheckBox();
deobfOn.setSelected(settings.isDeobfuscationOn());
deobfOn.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
settings.setDeobfuscationOn(e.getStateChange() == ItemEvent.SELECTED);
needReload();
}
deobfOn.addItemListener(e -> {
settings.setDeobfuscationOn(e.getStateChange() == ItemEvent.SELECTED);
needReload();
});
JCheckBox deobfForce = new JCheckBox();
deobfForce.setSelected(settings.isDeobfuscationForceSave());
deobfForce.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
settings.setDeobfuscationForceSave(e.getStateChange() == ItemEvent.SELECTED);
needReload();
}
deobfForce.addItemListener(e -> {
settings.setDeobfuscationForceSave(e.getStateChange() == ItemEvent.SELECTED);
needReload();
});
final JSpinner minLen = new JSpinner();
JSpinner minLen = new JSpinner();
minLen.setValue(settings.getDeobfuscationMinLength());
minLen.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
settings.setDeobfuscationMinLength((Integer) minLen.getValue());
needReload();
}
minLen.addChangeListener(e -> {
settings.setDeobfuscationMinLength((Integer) minLen.getValue());
needReload();
});
final JSpinner maxLen = new JSpinner();
JSpinner maxLen = new JSpinner();
maxLen.setValue(settings.getDeobfuscationMaxLength());
maxLen.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
settings.setDeobfuscationMaxLength((Integer) maxLen.getValue());
needReload();
}
maxLen.addChangeListener(e -> {
settings.setDeobfuscationMaxLength((Integer) maxLen.getValue());
needReload();
});
JCheckBox deobfSourceAlias = new JCheckBox();
deobfSourceAlias.setSelected(settings.isDeobfuscationUseSourceNameAsAlias());
deobfSourceAlias.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
settings.setDeobfuscationUseSourceNameAsAlias(e.getStateChange() == ItemEvent.SELECTED);
needReload();
}
deobfSourceAlias.addItemListener(e -> {
settings.setDeobfuscationUseSourceNameAsAlias(e.getStateChange() == ItemEvent.SELECTED);
needReload();
});
SettingsGroup deobfGroup = new SettingsGroup(NLS.str("preferences.deobfuscation"));
......@@ -206,7 +193,7 @@ public class JadxSettingsWindow extends JDialog {
});
EditorTheme[] editorThemes = CodeArea.getAllThemes();
final JComboBox<EditorTheme> themesCbx = new JComboBox<>(editorThemes);
JComboBox<EditorTheme> themesCbx = new JComboBox<>(editorThemes);
for (EditorTheme theme: editorThemes) {
if (theme.getPath().equals(settings.getEditorThemePath())) {
themesCbx.setSelectedItem(theme);
......@@ -230,66 +217,49 @@ public class JadxSettingsWindow extends JDialog {
private SettingsGroup makeDecompilationGroup() {
JCheckBox fallback = new JCheckBox();
fallback.setSelected(settings.isFallbackMode());
fallback.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
settings.setFallbackMode(e.getStateChange() == ItemEvent.SELECTED);
needReload();
}
fallback.addItemListener(e -> {
settings.setFallbackMode(e.getStateChange() == ItemEvent.SELECTED);
needReload();
});
JCheckBox showInconsistentCode = new JCheckBox();
showInconsistentCode.setSelected(settings.isShowInconsistentCode());
showInconsistentCode.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
settings.setShowInconsistentCode(e.getStateChange() == ItemEvent.SELECTED);
needReload();
}
showInconsistentCode.addItemListener(e -> {
settings.setShowInconsistentCode(e.getStateChange() == ItemEvent.SELECTED);
needReload();
});
JCheckBox resourceDecode = new JCheckBox();
resourceDecode.setSelected(settings.isSkipResources());
resourceDecode.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
settings.setSkipResources(e.getStateChange() == ItemEvent.SELECTED);
needReload();
}
resourceDecode.addItemListener(e -> {
settings.setSkipResources(e.getStateChange() == ItemEvent.SELECTED);
needReload();
});
SpinnerNumberModel spinnerModel = new SpinnerNumberModel(
settings.getThreadsCount(), 1, Runtime.getRuntime().availableProcessors() * 2, 1);
final JSpinner threadsCount = new JSpinner(spinnerModel);
threadsCount.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
settings.setThreadsCount((Integer) threadsCount.getValue());
needReload();
}
JSpinner threadsCount = new JSpinner(spinnerModel);
threadsCount.addChangeListener(e -> {
settings.setThreadsCount((Integer) threadsCount.getValue());
needReload();
});
JCheckBox autoStartJobs = new JCheckBox();
autoStartJobs.setSelected(settings.isAutoStartJobs());
autoStartJobs.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
settings.setAutoStartJobs(e.getStateChange() == ItemEvent.SELECTED);
}
});
autoStartJobs.addItemListener(e -> settings.setAutoStartJobs(e.getStateChange() == ItemEvent.SELECTED));
JCheckBox escapeUnicode = new JCheckBox();
escapeUnicode.setSelected(settings.escapeUnicode());
escapeUnicode.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
settings.setEscapeUnicode(e.getStateChange() == ItemEvent.SELECTED);
needReload();
}
escapeUnicode.addItemListener(e -> {
settings.setEscapeUnicode(e.getStateChange() == ItemEvent.SELECTED);
needReload();
});
JCheckBox replaceConsts = new JCheckBox();
replaceConsts.setSelected(settings.isReplaceConsts());
replaceConsts.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
settings.setReplaceConsts(e.getStateChange() == ItemEvent.SELECTED);
needReload();
}
replaceConsts.addItemListener(e -> {
settings.setReplaceConsts(e.getStateChange() == ItemEvent.SELECTED);
needReload();
});
SettingsGroup other = new SettingsGroup(NLS.str("preferences.decompile"));
......@@ -304,33 +274,35 @@ public class JadxSettingsWindow extends JDialog {
}
private SettingsGroup makeOtherGroup() {
JComboBox<LangLocale> languageCbx = new JComboBox<>(NLS.getI18nLocales());
for (LangLocale locale: NLS.getI18nLocales()) {
if (locale.equals(settings.getLangLocale())) {
languageCbx.setSelectedItem(locale);
break;
}
}
languageCbx.addActionListener(e -> settings.setLangLocale((LangLocale) languageCbx.getSelectedItem()));
JCheckBox update = new JCheckBox();
update.setSelected(settings.isCheckForUpdates());
update.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
settings.setCheckForUpdates(e.getStateChange() == ItemEvent.SELECTED);
}
});
update.addItemListener(e -> settings.setCheckForUpdates(e.getStateChange() == ItemEvent.SELECTED));
JCheckBox cfg = new JCheckBox();
cfg.setSelected(settings.isCfgOutput());
cfg.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
settings.setCfgOutput(e.getStateChange() == ItemEvent.SELECTED);
needReload();
}
cfg.addItemListener(e -> {
settings.setCfgOutput(e.getStateChange() == ItemEvent.SELECTED);
needReload();
});
JCheckBox rawCfg = new JCheckBox();
rawCfg.setSelected(settings.isRawCfgOutput());
rawCfg.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
settings.setRawCfgOutput(e.getStateChange() == ItemEvent.SELECTED);
needReload();
}
rawCfg.addItemListener(e -> {
settings.setRawCfgOutput(e.getStateChange() == ItemEvent.SELECTED);
needReload();
});
SettingsGroup other = new SettingsGroup(NLS.str("preferences.other"));
other.addRow(NLS.str("preferences.language"), languageCbx);
other.addRow(NLS.str("preferences.check_for_updates"), update);
other.addRow(NLS.str("preferences.cfg"), cfg);
other.addRow(NLS.str("preferences.raw_cfg"), rawCfg);
......
......@@ -55,7 +55,7 @@ class AboutDialog extends JDialog {
setModalityType(ModalityType.APPLICATION_MODAL);
setTitle("About JADX");
setTitle(NLS.str("about_dialog.title"));
pack();
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
setLocationRelativeTo(null);
......
......@@ -301,7 +301,10 @@ public abstract class CommonSearchDialog extends JDialog {
protected static class ResultsModel extends AbstractTableModel {
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 ResultsTableCellRenderer renderer;
......@@ -525,7 +528,7 @@ public abstract class CommonSearchDialog extends JDialog {
TextSearchIndex textIndex = cache.getTextIndex();
if (textIndex == null) {
warnLabel.setText("Index not initialized, search will be disabled!");
warnLabel.setText(NLS.str("msg.index_not_initialized"));
warnLabel.setVisible(true);
}
}
......
......@@ -40,7 +40,7 @@ class LogViewer extends JDialog {
level = LEVEL_ITEMS[i];
registerLogListener();
});
JLabel levelLabel = new JLabel(NLS.str("log.level"));
JLabel levelLabel = new JLabel(NLS.str("log_viewer.log_level"));
levelLabel.setLabelFor(cb);
controlPane.add(levelLabel);
controlPane.add(cb);
......@@ -56,7 +56,7 @@ class LogViewer extends JDialog {
contentPane.add(scrollPane, BorderLayout.CENTER);
contentPane.add(close, BorderLayout.PAGE_END);
setTitle("Log Viewer");
setTitle(NLS.str("log_viewer.title"));
pack();
setSize(800, 600);
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
......
......@@ -154,7 +154,7 @@ public class MainWindow extends JFrame {
String[] exts = {"apk", "dex", "jar", "class", "zip", "aar", "arsc"};
String description = "supported files: " + Arrays.toString(exts).replace('[', '(').replace(']', ')');
fileChooser.setFileFilter(new FileNameExtensionFilter(description, exts));
fileChooser.setToolTipText(NLS.str("file.open"));
fileChooser.setToolTipText(NLS.str("file.open_action"));
String currentDirectory = settings.getLastOpenFilePath();
if (!currentDirectory.isEmpty()) {
fileChooser.setCurrentDirectory(new File(currentDirectory));
......@@ -409,7 +409,7 @@ public class MainWindow extends JFrame {
clsSearchAction.putValue(Action.SHORT_DESCRIPTION, NLS.str("menu.class_search"));
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
public void actionPerformed(ActionEvent e) {
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;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Vector;
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 {
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 static void load(Locale locale) {
messages = ResourceBundle.getBundle("i18n/Messages", locale);
private static void load(LangLocale 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) {
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.view=View
menu.recent_files=Recent Files
......@@ -9,14 +11,14 @@ menu.navigation=Navigation
menu.text_search=Text search
menu.class_search=Class search
menu.tools=Tools
menu.deobfuscation=Deobfuscation
menu.log=Log Viewer
menu.help=Help
menu.about=About
menu.update_label=New version %s available!
file.open=Open file
file.open_action=Open file...
file.open_title=Open file
file.open_action=Open file
file.save_all=Save all
file.export_gradle=Save as gradle project
file.save_all_msg=Select directory for save decompiled sources
......@@ -45,26 +47,34 @@ nav.forward=Forward
search_dialog.open=Open
search_dialog.cancel=Cancel
search_dialog.open_by_name=Search for text\:
search_dialog.search_in=Search definitions of \:
search_dialog.open_by_name=Search for text:
search_dialog.search_in=Search definitions of:
search_dialog.class=Class
search_dialog.method=Method
search_dialog.field=Field
search_dialog.code=Code
search_dialog.options=Search options \:
search_dialog.options=Search options:
search_dialog.ignorecase=Case insensitive
search_dialog.next_page=Show next 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.label=Usage for:
log_viewer.title=Log Viewer
log_viewer.log_level=Log level:
about_dialog.title=About JADX
preferences.title=Preferences
preferences.deobfuscation=Deobfuscation
preferences.editor=Editor
preferences.decompile=Decompilation
preferences.other=Other
preferences.language=Language
preferences.check_for_updates=Check for updates on startup
preferences.fallback=Fallback mode (simple dump)
preferences.showInconsistentCode=Show inconsistent code
......@@ -90,9 +100,10 @@ preferences.reset_message=Reset settings to default values?
preferences.reset_title=Reset settings
msg.open_file=Please open file
msg.saving_sources=Saving sources
log.level=Log level:
msg.saving_sources=Saving sources...
msg.language_changed_title=Language changed
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.redo=Redo
......
language.name=中文(简体)
menu.file=文件
menu.view=视图
menu.recent_files=最近打开文件
......@@ -82,6 +84,8 @@ preferences.reset_title=重置设置
msg.open_file=请打开文件
msg.saving_sources=保存资源
log.level=日志等级:
msg.language_changed_title=语言已更改
msg.language_changed=在下次启动时将会显示新的语言。
popup.undo=撤销
popup.redo=重复上一次操作
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册