From 74a72a5ce01ea9b81353ef39ab40a9f18e7bb18d Mon Sep 17 00:00:00 2001 From: Ahmed Ashour Date: Fri, 12 Apr 2019 17:08:50 +0200 Subject: [PATCH] feat: add options to configure "renaming" (#570) (PR #582) --- .../src/main/java/jadx/cli/JadxCLIArgs.java | 89 ++++++++++++++++++- .../java/jadx/cli/RenameConverterTest.java | 49 ++++++++++ .../src/main/java/jadx/api/JadxArgs.java | 82 +++++++++++++---- .../main/java/jadx/core/deobf/NameMapper.java | 6 +- .../jadx/core/dex/visitors/RenameVisitor.java | 41 +++++---- .../jadx/gui/settings/JadxSettingsWindow.java | 59 +++++++++++- .../src/main/java/jadx/gui/ui/MainWindow.java | 12 ++- .../resources/i18n/Messages_en_US.properties | 6 ++ .../resources/i18n/Messages_es_ES.properties | 6 ++ .../resources/i18n/Messages_zh_CN.properties | 6 ++ 10 files changed, 309 insertions(+), 47 deletions(-) create mode 100644 jadx-cli/src/test/java/jadx/cli/RenameConverterTest.java diff --git a/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java b/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java index a8b8b151..71b39ec8 100644 --- a/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java +++ b/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java @@ -1,16 +1,23 @@ package jadx.cli; import java.util.ArrayList; +import java.util.EnumSet; +import java.util.HashSet; import java.util.List; +import java.util.Locale; +import java.util.Set; import java.util.stream.Collectors; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.core.Appender; -import com.beust.jcommander.Parameter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.beust.jcommander.IStringConverter; +import com.beust.jcommander.Parameter; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.Appender; import jadx.api.JadxArgs; +import jadx.api.JadxArgs.RENAME; import jadx.api.JadxDecompiler; import jadx.core.utils.exceptions.JadxException; import jadx.core.utils.files.FileUtils; @@ -83,6 +90,10 @@ public class JadxCLIArgs { @Parameter(names = {"-f", "--fallback"}, description = "make simple dump (using goto instead of 'if', 'for', etc)") protected boolean fallbackMode = false; + @Parameter(names = {"--rename-flags"}, description = "what to rename, comma-separated, 'case' for system case sensitivity, 'valid' for java identifiers, 'printable' characters, 'none' or 'all'", + converter = RenameConverter.class) + protected Set renameFlags = EnumSet.allOf(RENAME.class); + @Parameter(names = {"-v", "--verbose"}, description = "verbose output") protected boolean verbose = false; @@ -164,6 +175,9 @@ public class JadxCLIArgs { args.setExportAsGradleProject(exportAsGradleProject); args.setUseImports(useImports); args.setDebugInfo(debugInfo); + args.setRenameCaseSensitive(isRenameCaseSensitive()); + args.setRenameValid(isRenameValid()); + args.setRenamePrintable(isRenamePrintable()); return args; } @@ -254,4 +268,73 @@ public class JadxCLIArgs { public boolean isExportAsGradleProject() { return exportAsGradleProject; } + + public boolean isRenameCaseSensitive() { + return renameFlags.contains(RENAME.CASE); + } + + public void setRenameCaseSensitive(boolean renameCase) { + if (renameCase && !isRenameCaseSensitive()) { + renameFlags.add(RENAME.CASE); + } else if (!renameCase && isRenameCaseSensitive()) { + renameFlags.remove(RENAME.CASE); + } + } + + public boolean isRenameValid() { + return renameFlags.contains(RENAME.VALID); + } + + public void setRenameValid(boolean renameValid) { + if (renameValid && !isRenameValid()) { + renameFlags.add(RENAME.VALID); + } else if (!renameValid && isRenameValid()) { + renameFlags.remove(RENAME.VALID); + } + } + + public boolean isRenamePrintable() { + return renameFlags.contains(RENAME.PRINTABLE); + } + + public void setRenamePrintable(boolean renamePrintable) { + if (renamePrintable && !isRenamePrintable()) { + renameFlags.add(RENAME.PRINTABLE); + } else if (!renamePrintable && isRenamePrintable()) { + renameFlags.remove(RENAME.PRINTABLE); + } + } + + static class RenameConverter implements IStringConverter> { + + private final String paramName; + + RenameConverter(String paramName) { + this.paramName = paramName; + } + + @Override + public Set convert(String value) { + Set set = new HashSet<>(); + if (value.equalsIgnoreCase("ALL")) { + set.add(RENAME.CASE); + set.add(RENAME.VALID); + set.add(RENAME.PRINTABLE); + } else if (!value.equalsIgnoreCase("NONE")) { + for (String s : value.split(",")) { + try { + set.add(RENAME.valueOf(s.toUpperCase(Locale.ROOT))); + } catch (IllegalArgumentException e) { + String values = "'" + RENAME.CASE + + "', '" + RENAME.VALID + + "' and '" + RENAME.PRINTABLE + '\''; + throw new IllegalArgumentException( + s + " is unknown for parameter " + paramName + + ", possible values are " + values.toLowerCase(Locale.ROOT)); + } + } + } + return set; + } + } } diff --git a/jadx-cli/src/test/java/jadx/cli/RenameConverterTest.java b/jadx-cli/src/test/java/jadx/cli/RenameConverterTest.java new file mode 100644 index 00000000..961a49ea --- /dev/null +++ b/jadx-cli/src/test/java/jadx/cli/RenameConverterTest.java @@ -0,0 +1,49 @@ +package jadx.cli; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Set; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import jadx.api.JadxArgs.RENAME; +import jadx.cli.JadxCLIArgs.RenameConverter; + +public class RenameConverterTest { + + private RenameConverter converter; + + @BeforeEach + public void init() { + converter = new RenameConverter("someParam"); + } + + @Test + public void all() { + Set set = converter.convert("all"); + assertEquals(3, set.size()); + assertTrue(set.contains(RENAME.CASE)); + assertTrue(set.contains(RENAME.VALID)); + assertTrue(set.contains(RENAME.PRINTABLE)); + } + + @Test + public void none() { + Set set = converter.convert("none"); + assertTrue(set.isEmpty()); + } + + @Test + public void wrong() { + IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, + () -> converter.convert("wrong"), + "Expected convert() to throw, but it didn't"); + + assertEquals("wrong is unknown for parameter someParam, " + + "possible values are 'case', 'valid' and 'printable'", + thrown.getMessage()); + } +} diff --git a/jadx-core/src/main/java/jadx/api/JadxArgs.java b/jadx-core/src/main/java/jadx/api/JadxArgs.java index 8c17f2f7..df4de620 100644 --- a/jadx-core/src/main/java/jadx/api/JadxArgs.java +++ b/jadx-core/src/main/java/jadx/api/JadxArgs.java @@ -3,7 +3,9 @@ package jadx.api; import java.io.File; import java.util.ArrayList; import java.util.Collections; +import java.util.EnumSet; import java.util.List; +import java.util.Set; public class JadxArgs { @@ -30,11 +32,11 @@ public class JadxArgs { private boolean useImports = true; private boolean debugInfo = true; - private boolean isSkipResources = false; - private boolean isSkipSources = false; + private boolean skipResources = false; + private boolean skipSources = false; - private boolean isDeobfuscationOn = false; - private boolean isDeobfuscationForceSave = false; + private boolean deobfuscationOn = false; + private boolean deobfuscationForceSave = false; private boolean useSourceNameAsClassAlias = false; private int deobfuscationMinLength = 0; @@ -45,7 +47,11 @@ public class JadxArgs { private boolean respectBytecodeAccModifiers = false; private boolean exportAsGradleProject = false; - private boolean isFsCaseSensitive; + private boolean fsCaseSensitive; + + public enum RENAME {CASE, VALID, PRINTABLE} + + private Set renameFlags = EnumSet.allOf(RENAME.class); public JadxArgs() { // use default options @@ -150,35 +156,35 @@ public class JadxArgs { } public boolean isSkipResources() { - return isSkipResources; + return skipResources; } public void setSkipResources(boolean skipResources) { - isSkipResources = skipResources; + this.skipResources = skipResources; } public boolean isSkipSources() { - return isSkipSources; + return skipSources; } public void setSkipSources(boolean skipSources) { - isSkipSources = skipSources; + this.skipSources = skipSources; } public boolean isDeobfuscationOn() { - return isDeobfuscationOn; + return deobfuscationOn; } public void setDeobfuscationOn(boolean deobfuscationOn) { - isDeobfuscationOn = deobfuscationOn; + this.deobfuscationOn = deobfuscationOn; } public boolean isDeobfuscationForceSave() { - return isDeobfuscationForceSave; + return deobfuscationForceSave; } public void setDeobfuscationForceSave(boolean deobfuscationForceSave) { - isDeobfuscationForceSave = deobfuscationForceSave; + this.deobfuscationForceSave = deobfuscationForceSave; } public boolean isUseSourceNameAsClassAlias() { @@ -238,11 +244,47 @@ public class JadxArgs { } public boolean isFsCaseSensitive() { - return isFsCaseSensitive; + return fsCaseSensitive; } public void setFsCaseSensitive(boolean fsCaseSensitive) { - isFsCaseSensitive = fsCaseSensitive; + this.fsCaseSensitive = fsCaseSensitive; + } + + public boolean isRenameCaseSensitive() { + return renameFlags.contains(RENAME.CASE); + } + + public void setRenameCaseSensitive(boolean renameCaseSensitive) { + if (renameCaseSensitive && !isRenameCaseSensitive()) { + renameFlags.add(RENAME.CASE); + } else if (!renameCaseSensitive && isRenameCaseSensitive()) { + renameFlags.remove(RENAME.CASE); + } + } + + public boolean isRenameValid() { + return renameFlags.contains(RENAME.VALID); + } + + public void setRenameValid(boolean renameValid) { + if (renameValid && !isRenameValid()) { + renameFlags.add(RENAME.VALID); + } else if (!renameValid && isRenameValid()) { + renameFlags.remove(RENAME.VALID); + } + } + + public boolean isRenamePrintable() { + return renameFlags.contains(RENAME.PRINTABLE); + } + + public void setRenamePrintable(boolean renamePrintable) { + if (renamePrintable && !isRenamePrintable()) { + renameFlags.add(RENAME.PRINTABLE); + } else if (!renamePrintable && isRenamePrintable()) { + renameFlags.remove(RENAME.PRINTABLE); + } } @Override @@ -257,10 +299,10 @@ public class JadxArgs { ", fallbackMode=" + fallbackMode + ", showInconsistentCode=" + showInconsistentCode + ", useImports=" + useImports + - ", isSkipResources=" + isSkipResources + - ", isSkipSources=" + isSkipSources + - ", isDeobfuscationOn=" + isDeobfuscationOn + - ", isDeobfuscationForceSave=" + isDeobfuscationForceSave + + ", skipResources=" + skipResources + + ", skipSources=" + skipSources + + ", deobfuscationOn=" + deobfuscationOn + + ", deobfuscationForceSave=" + deobfuscationForceSave + ", useSourceNameAsClassAlias=" + useSourceNameAsClassAlias + ", deobfuscationMinLength=" + deobfuscationMinLength + ", deobfuscationMaxLength=" + deobfuscationMaxLength + @@ -268,6 +310,8 @@ public class JadxArgs { ", replaceConsts=" + replaceConsts + ", respectBytecodeAccModifiers=" + respectBytecodeAccModifiers + ", exportAsGradleProject=" + exportAsGradleProject + + ", fsCaseSensitive=" + fsCaseSensitive + + ", renameFlags=" + renameFlags + '}'; } } diff --git a/jadx-core/src/main/java/jadx/core/deobf/NameMapper.java b/jadx-core/src/main/java/jadx/core/deobf/NameMapper.java index 8d6ba622..021ca965 100644 --- a/jadx-core/src/main/java/jadx/core/deobf/NameMapper.java +++ b/jadx-core/src/main/java/jadx/core/deobf/NameMapper.java @@ -80,15 +80,13 @@ public class NameMapper { public static boolean isValidIdentifier(String str) { return notEmpty(str) && !isReserved(str) - && VALID_JAVA_IDENTIFIER.matcher(str).matches() - && isAllCharsPrintable(str); + && VALID_JAVA_IDENTIFIER.matcher(str).matches(); } public static boolean isValidFullIdentifier(String str) { return notEmpty(str) && !isReserved(str) - && VALID_JAVA_FULL_IDENTIFIER.matcher(str).matches() - && isAllCharsPrintable(str); + && VALID_JAVA_FULL_IDENTIFIER.matcher(str).matches(); } public static boolean isValidIdentifierStart(int codePoint) { diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/RenameVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/RenameVisitor.java index 5fb44813..ae2f1b5a 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/RenameVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/RenameVisitor.java @@ -43,17 +43,17 @@ public class RenameVisitor extends AbstractVisitor { if (deobfuscationOn) { deobfuscator.execute(); } - checkClasses(root, args.isFsCaseSensitive()); + checkClasses(root, args); } - private void checkClasses(RootNode root, boolean caseSensitive) { + private void checkClasses(RootNode root, JadxArgs args) { List classes = root.getClasses(true); for (ClassNode cls : classes) { - checkClassName(cls); - checkFields(cls); - checkMethods(cls); + checkClassName(cls, args); + checkFields(cls, args); + checkMethods(cls, args); } - if (!caseSensitive) { + if (!args.isFsCaseSensitive() && args.isRenameCaseSensitive()) { Set clsFullPaths = new HashSet<>(classes.size()); for (ClassNode cls : classes) { ClassInfo clsInfo = cls.getClassInfo(); @@ -69,12 +69,12 @@ public class RenameVisitor extends AbstractVisitor { } } - private void checkClassName(ClassNode cls) { + private void checkClassName(ClassNode cls, JadxArgs args) { ClassInfo classInfo = cls.getClassInfo(); ClassInfo alias = classInfo.getAlias(); String clsName = alias.getShortName(); - String newShortName = fixClsShortName(clsName); + String newShortName = fixClsShortName(args, clsName); if (!newShortName.equals(clsName)) { classInfo.rename(cls.root(), alias.makeFullClsName(newShortName, true)); alias = classInfo.getAlias(); @@ -86,35 +86,42 @@ public class RenameVisitor extends AbstractVisitor { } } - private String fixClsShortName(String clsName) { + private String fixClsShortName(JadxArgs args, String clsName) { char firstChar = clsName.charAt(0); - if (Character.isDigit(firstChar)) { + boolean renameValid = args.isRenameValid(); + if (Character.isDigit(firstChar) && renameValid) { return Consts.ANONYMOUS_CLASS_PREFIX + NameMapper.removeInvalidCharsMiddle(clsName); } - if (firstChar == '$') { + if (firstChar == '$' && renameValid) { return 'C' + NameMapper.removeInvalidCharsMiddle(clsName); } - String cleanClsName = NameMapper.removeInvalidChars(clsName, "C"); - if (!NameMapper.isValidIdentifier(cleanClsName)) { + String cleanClsName = args.isRenamePrintable() + ? NameMapper.removeInvalidChars(clsName, "C") + : clsName; + if (renameValid && !NameMapper.isValidIdentifier(cleanClsName)) { return 'C' + cleanClsName; } return cleanClsName; } - private void checkFields(ClassNode cls) { + private void checkFields(ClassNode cls, JadxArgs args) { Set names = new HashSet<>(); for (FieldNode field : cls.getFields()) { FieldInfo fieldInfo = field.getFieldInfo(); String fieldName = fieldInfo.getAlias(); - if (!names.add(fieldName) || !NameMapper.isValidIdentifier(fieldName)) { + if (!names.add(fieldName) + || (args.isRenameValid() && !NameMapper.isValidIdentifier(fieldName)) + || (args.isRenamePrintable() && !NameMapper.isAllCharsPrintable(fieldName))) { deobfuscator.forceRenameField(field); } } } - private void checkMethods(ClassNode cls) { + private void checkMethods(ClassNode cls, JadxArgs args) { for (MethodNode mth : cls.getMethods()) { - if (!NameMapper.isValidIdentifier(mth.getAlias())) { + String alias = mth.getAlias(); + if (args.isRenameValid() && !NameMapper.isValidIdentifier(alias) + || (args.isRenamePrintable() && !NameMapper.isAllCharsPrintable(alias))) { deobfuscator.forceRenameMethod(mth); } } diff --git a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsWindow.java b/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsWindow.java index 883237c4..06ae6c5d 100644 --- a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsWindow.java +++ b/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsWindow.java @@ -1,22 +1,45 @@ package jadx.gui.settings; -import javax.swing.*; -import java.awt.*; +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; import java.awt.event.ItemEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.Arrays; import java.util.Collection; +import javax.swing.BorderFactory; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSpinner; +import javax.swing.ScrollPaneConstants; +import javax.swing.SpinnerNumberModel; +import javax.swing.SwingConstants; +import javax.swing.WindowConstants; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import say.swing.JFontChooser; import jadx.gui.ui.MainWindow; import jadx.gui.ui.codearea.EditorTheme; import jadx.gui.utils.LangLocale; import jadx.gui.utils.NLS; import jadx.gui.utils.Utils; +import say.swing.JFontChooser; public class JadxSettingsWindow extends JDialog { private static final long serialVersionUID = -1804570470377354148L; @@ -51,6 +74,7 @@ public class JadxSettingsWindow extends JDialog { panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS)); panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); panel.add(makeDeobfuscationGroup()); + panel.add(makeRenameGroup()); panel.add(makeDecompilationGroup()); panel.add(makeProjectGroup()); panel.add(makeEditorGroup()); @@ -165,6 +189,35 @@ public class JadxSettingsWindow extends JDialog { return deobfGroup; } + private SettingsGroup makeRenameGroup() { + JCheckBox renameCaseSensitive = new JCheckBox(); + renameCaseSensitive.setSelected(settings.isRenameCaseSensitive()); + renameCaseSensitive.addItemListener(e -> { + settings.setRenameCaseSensitive(e.getStateChange() == ItemEvent.SELECTED); + needReload(); + }); + + JCheckBox renameValid = new JCheckBox(); + renameValid.setSelected(settings.isRenameValid()); + renameValid.addItemListener(e -> { + settings.setRenameValid(e.getStateChange() == ItemEvent.SELECTED); + needReload(); + }); + + JCheckBox renamePrintable = new JCheckBox(); + renamePrintable.setSelected(settings.isRenamePrintable()); + renamePrintable.addItemListener(e -> { + settings.setRenamePrintable(e.getStateChange() == ItemEvent.SELECTED); + needReload(); + }); + + SettingsGroup group = new SettingsGroup(NLS.str("preferences.rename")); + group.addRow(NLS.str("preferences.rename_case"), renameCaseSensitive); + group.addRow(NLS.str("preferences.rename_valid"), renameValid); + group.addRow(NLS.str("preferences.rename_printable"), renamePrintable); + return group; + } + private void enableComponentList(Collection connectedComponents, boolean enabled) { connectedComponents.forEach(comp -> comp.setEnabled(enabled)); } diff --git a/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java b/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java index b1e68552..c89d7a51 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java @@ -166,6 +166,7 @@ public class MainWindow extends JFrame { setLocationRelativeTo(null); setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); addWindowListener(new WindowAdapter() { + @Override public void windowClosing(WindowEvent e) { closeWindow(); } @@ -386,6 +387,16 @@ public class MainWindow extends JFrame { } private void saveAll(boolean export) { + JadxArgs decompilerArgs = wrapper.getArgs(); + if ((!decompilerArgs.isFsCaseSensitive() && !decompilerArgs.isRenameCaseSensitive()) + || !decompilerArgs.isRenameValid() || !decompilerArgs.isRenamePrintable()) { + JOptionPane.showMessageDialog( + this, + NLS.str("msg.rename_disabled", settings.getLangLocale()), + NLS.str("msg.rename_disabled_title", settings.getLangLocale()), + JOptionPane.INFORMATION_MESSAGE + ); + } JFileChooser fileChooser = new JFileChooser(); fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); fileChooser.setToolTipText(NLS.str("file.save_all_msg")); @@ -397,7 +408,6 @@ public class MainWindow extends JFrame { int ret = fileChooser.showSaveDialog(mainPanel); if (ret == JFileChooser.APPROVE_OPTION) { - JadxArgs decompilerArgs = wrapper.getArgs(); decompilerArgs.setExportAsGradleProject(export); if (export) { decompilerArgs.setSkipSources(false); diff --git a/jadx-gui/src/main/resources/i18n/Messages_en_US.properties b/jadx-gui/src/main/resources/i18n/Messages_en_US.properties index 38771cad..f82cab8a 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_en_US.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_en_US.properties @@ -113,6 +113,10 @@ preferences.cancel=Cancel preferences.reset=Reset preferences.reset_message=Reset settings to default values? preferences.reset_title=Reset settings +preferences.rename=Rename +preferences.rename_case=System case sensitivity +preferences.rename_valid=To be valid identifier +preferences.rename_printable=To be printable msg.open_file=Please open file msg.saving_sources=Saving sources... @@ -121,6 +125,8 @@ msg.language_changed=New language will be displayed the next time application st msg.index_not_initialized=Index not initialized, search will be disabled! msg.project_error_title=Error msg.project_error=Project could not be loaded +msg.rename_disabled_title=Rename disabled +msg.rename_disabled=Some of rename settings are disabled, please take this into consideration popup.undo=Undo popup.redo=Redo diff --git a/jadx-gui/src/main/resources/i18n/Messages_es_ES.properties b/jadx-gui/src/main/resources/i18n/Messages_es_ES.properties index 8d13d8ad..1013b948 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_es_ES.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_es_ES.properties @@ -113,6 +113,10 @@ preferences.cancel=Cancelar preferences.reset=Reestablecer preferences.reset_message=¿Reestablecer preferencias a valores por defecto? preferences.reset_title=Reestablecer preferencias +#preferences.rename= +#preferences.rename_case= +#preferences.rename_valid= +#preferences.rename_printable= msg.open_file=Por favor, abra un archivo msg.saving_sources=Guardando fuente... @@ -121,6 +125,8 @@ msg.language_changed=El nuevo idioma se mostrará la próxima vez que la aplicac msg.index_not_initialized=Índice no inicializado, ¡la bósqueda se desactivará! #msg.project_error_title= #msg.project_error= +#msg.rename_disabled_title= +#msg.rename_disabled= popup.undo=Deshacer popup.redo=Rehacer diff --git a/jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties b/jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties index 20982886..f92467fa 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties @@ -113,6 +113,10 @@ preferences.cancel=取消 preferences.reset=重置 preferences.reset_message=要恢复默认设置吗? preferences.reset_title=重置设置 +#preferences.rename= +#preferences.rename_case= +#preferences.rename_valid= +#preferences.rename_printable= msg.open_file=请打开文件 msg.saving_sources=正在导出源代码... @@ -121,6 +125,8 @@ msg.language_changed=在下次启动时将会显示新的语言。 msg.index_not_initialized=索引尚未初始化,无法进行搜索! #msg.project_error_title= #msg.project_error= +#msg.rename_disabled_title= +#msg.rename_disabled= popup.undo=撤销 popup.redo=重做 -- GitLab