diff --git a/plugins/java-i18n/src/META-INF/plugin.xml b/plugins/java-i18n/src/META-INF/plugin.xml
index eb586538f7fa..bda0cc96c63e 100644
--- a/plugins/java-i18n/src/META-INF/plugin.xml
+++ b/plugins/java-i18n/src/META-INF/plugin.xml
@@ -76,6 +76,7 @@
Edit property value at usage location, where it's displayed in folding placeholder
+
diff --git a/plugins/java-i18n/src/com/intellij/codeInspection/i18n/I18nInspection.java b/plugins/java-i18n/src/com/intellij/codeInspection/i18n/I18nInspection.java
index 67ff2fbcc02d..598f31eba1fe 100644
--- a/plugins/java-i18n/src/com/intellij/codeInspection/i18n/I18nInspection.java
+++ b/plugins/java-i18n/src/com/intellij/codeInspection/i18n/I18nInspection.java
@@ -17,6 +17,7 @@ import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.WriteExternalException;
+import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
@@ -519,27 +520,33 @@ public class I18nInspection extends AbstractBaseUastLocalInspectionTool implemen
List fixes = new ArrayList<>();
- if (myOnTheFly && sourcePsi instanceof PsiLiteralExpression) {
- if (I18nizeConcatenationQuickFix.getEnclosingLiteralConcatenation(sourcePsi) != null) {
- fixes.add(new I18nizeConcatenationQuickFix());
- }
- fixes.add(new I18nizeQuickFix());
+ if (sourcePsi instanceof PsiLiteralExpression) {
+ if (myOnTheFly) {
+ if (I18nizeConcatenationQuickFix.getEnclosingLiteralConcatenation(sourcePsi) != null) {
+ fixes.add(new I18nizeConcatenationQuickFix());
+ }
+ fixes.add(new I18nizeQuickFix());
- if (!isNotConstantFieldInitializer((PsiExpression)sourcePsi)) {
- fixes.add(createIntroduceConstantFix());
- }
+ if (!isNotConstantFieldInitializer((PsiExpression)sourcePsi)) {
+ fixes.add(createIntroduceConstantFix());
+ }
- if (PsiUtil.isLanguageLevel5OrHigher(sourcePsi)) {
- final JavaPsiFacade facade = JavaPsiFacade.getInstance(myManager.getProject());
- for (PsiModifierListOwner element : nonNlsTargets) {
- if (!AnnotationUtil.isAnnotated(element, AnnotationUtil.NLS, CHECK_HIERARCHY | CHECK_EXTERNAL)) {
- if (!element.getManager().isInProject(element) ||
- facade.findClass(AnnotationUtil.NON_NLS, element.getResolveScope()) != null) {
- fixes.add(new NonNlsAnnotationProvider().createFix(element));
+ if (PsiUtil.isLanguageLevel5OrHigher(sourcePsi)) {
+ final JavaPsiFacade facade = JavaPsiFacade.getInstance(myManager.getProject());
+ for (PsiModifierListOwner element : nonNlsTargets) {
+ if (!AnnotationUtil.isAnnotated(element, AnnotationUtil.NLS, CHECK_HIERARCHY | CHECK_EXTERNAL)) {
+ if (!element.getManager().isInProject(element) ||
+ facade.findClass(AnnotationUtil.NON_NLS, element.getResolveScope()) != null) {
+ fixes.add(new NonNlsAnnotationProvider().createFix(element));
+ }
}
}
}
}
+ else if (Registry.is("i18n.for.idea.project") &&
+ I18nizeConcatenationQuickFix.getEnclosingLiteralConcatenation(sourcePsi) == null) {
+ fixes.add(new I18nizeBatchQuickFix());
+ }
}
LocalQuickFix[] farr = fixes.toArray(LocalQuickFix.EMPTY_ARRAY);
diff --git a/plugins/java-i18n/src/com/intellij/codeInspection/i18n/I18nizeBatchQuickFix.java b/plugins/java-i18n/src/com/intellij/codeInspection/i18n/I18nizeBatchQuickFix.java
new file mode 100644
index 000000000000..e0b558fa1c8b
--- /dev/null
+++ b/plugins/java-i18n/src/com/intellij/codeInspection/i18n/I18nizeBatchQuickFix.java
@@ -0,0 +1,239 @@
+// Copyright 2000-2017 JetBrains s.r.o.
+// Use of this source code is governed by the Apache 2.0 license that can be
+// found in the LICENSE file.
+package com.intellij.codeInspection.i18n;
+
+import com.intellij.codeInsight.CodeInsightBundle;
+import com.intellij.codeInspection.BatchQuickFix;
+import com.intellij.codeInspection.CommonProblemDescriptor;
+import com.intellij.codeInspection.ProblemDescriptor;
+import com.intellij.ide.fileTemplates.FileTemplate;
+import com.intellij.ide.fileTemplates.FileTemplateManager;
+import com.intellij.ide.fileTemplates.JavaTemplateUtil;
+import com.intellij.ide.util.PropertiesComponent;
+import com.intellij.lang.properties.PropertiesImplUtil;
+import com.intellij.lang.properties.psi.PropertiesFile;
+import com.intellij.lang.properties.references.I18nUtil;
+import com.intellij.lang.properties.references.I18nizeQuickFixDialog;
+import com.intellij.openapi.command.WriteCommandAction;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.ComboBox;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.ui.LabeledComponent;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.*;
+import com.intellij.ui.*;
+import com.intellij.ui.table.JBTable;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.ObjectUtils;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.ui.ItemRemovable;
+import gnu.trove.THashMap;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import javax.swing.table.AbstractTableModel;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+public class I18nizeBatchQuickFix extends I18nizeQuickFix implements BatchQuickFix {
+ private static final Logger LOG = Logger.getInstance(I18nizeBatchQuickFix.class);
+
+
+ @Override
+ public void applyFix(@NotNull Project project,
+ @NotNull CommonProblemDescriptor[] descriptors,
+ @NotNull List psiElementsToIgnore,
+ @Nullable Runnable refreshViews) {
+ List keyValuePairs = ContainerUtil.mapNotNull(descriptors, descriptor -> {
+ PsiElement psiElement = ((ProblemDescriptor)descriptor).getPsiElement();
+ if (psiElement instanceof PsiLiteralExpression &&
+ I18nizeConcatenationQuickFix.getEnclosingLiteralConcatenation(psiElement) == null) {
+ Object val = ((PsiLiteralExpression)psiElement).getValue();
+ if (val instanceof String) {
+ String value = StringUtil.escapeStringCharacters((String)val);
+ String key = I18nizeQuickFixDialog.suggestUniquePropertyKey(value, null, null);
+ return new ReplacementBean(key, value, (PsiLiteralExpression)psiElement);
+ }
+ }
+ return null;
+ });
+
+ if (keyValuePairs.isEmpty()) return;
+
+ I18NBatchDialog dialog = new I18NBatchDialog(project, keyValuePairs);
+ if (dialog.showAndGet()) {
+ PropertiesFile propertiesFile = dialog.getPropertiesFile();
+ PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();
+ FileTemplate template = FileTemplateManager.getInstance(project).getCodeTemplate(JavaTemplateUtil.TEMPLATE_I18NIZED_EXPRESSION);
+ List files = ContainerUtil.mapNotNull(keyValuePairs, bean -> bean.getExpression().getContainingFile());
+ if (files.isEmpty()) {
+ return;
+ }
+ files.add(propertiesFile.getContainingFile());
+
+ WriteCommandAction.runWriteCommandAction(project, getFamilyName(), null, () -> {
+ String bundleName = propertiesFile.getVirtualFile().getNameWithoutExtension();
+ for (ReplacementBean bean : keyValuePairs) {
+ JavaI18nUtil.DEFAULT_PROPERTY_CREATION_HANDLER
+ .createProperty(project, Collections.singletonList(propertiesFile), bean.getKey(), bean.getValue(), PsiExpression.EMPTY_ARRAY);
+ PsiElement literalExpression = bean.getExpression();
+ String i18NText = getI18NText(bean.getKey(), bean.getValue(), bundleName, template);
+ PsiExpression expression = factory.createExpressionFromText(i18NText, literalExpression);
+ literalExpression.replace(expression);
+ }
+ }, files.toArray(PsiFile.EMPTY_ARRAY));
+ }
+ }
+
+ private static String getI18NText(String key, String value, String bundleName, FileTemplate template) {
+ Map attributes = new THashMap<>();
+ attributes.put(JavaI18nizeQuickFixDialog.PROPERTY_KEY_OPTION_KEY, StringUtil.escapeStringCharacters(key));
+ attributes.put(JavaI18nizeQuickFixDialog.RESOURCE_BUNDLE_OPTION_KEY, bundleName);
+ attributes.put(JavaI18nizeQuickFixDialog.PROPERTY_VALUE_ATTR, StringUtil.escapeStringCharacters(value));
+
+ String text = null;
+ try {
+ text = template.getText(attributes);
+ }
+ catch (IOException e) {
+ LOG.error(e);
+ }
+ return text;
+ }
+
+ private static class I18NBatchDialog extends DialogWrapper {
+ private static final @NonNls String LAST_USED_PROPERTIES_FILE = "LAST_USED_PROPERTIES_FILE";
+
+ @NotNull private final Project myProject;
+ private final List myKeyValuePairs;
+ private JComboBox myPropertiesFile;
+
+ protected I18NBatchDialog(@NotNull Project project, List keyValuePairs) {
+ super(project, true);
+ myProject = project;
+ myKeyValuePairs = keyValuePairs;
+ setTitle(CodeInsightBundle.message("i18nize.dialog.title"));
+ init();
+ }
+
+ @Nullable
+ @Override
+ protected JComponent createNorthPanel() {
+ List files = I18nUtil.defaultSuggestPropertiesFiles(myProject);
+ myPropertiesFile = new ComboBox<>(ArrayUtil.toStringArray(files));
+ new ComboboxSpeedSearch(myPropertiesFile);
+ if (!files.isEmpty()) {
+ myPropertiesFile.setSelectedItem(ObjectUtils.notNull(PropertiesComponent.getInstance(myProject).getValue(LAST_USED_PROPERTIES_FILE),
+ files.get(0)));
+ }
+ LabeledComponent> component = new LabeledComponent<>();
+ component.setText("Property file:");
+ component.setComponent(myPropertiesFile);
+
+ return component;
+ }
+
+ protected PropertiesFile getPropertiesFile() {
+ Object selectedItem = myPropertiesFile.getSelectedItem();
+ if (selectedItem == null) return null;
+ String path = FileUtil.toSystemIndependentName((String)selectedItem);
+ VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByPath(path);
+ return virtualFile != null
+ ? PropertiesImplUtil.getPropertiesFile(PsiManager.getInstance(myProject).findFile(virtualFile))
+ : null;
+ }
+
+ @Nullable
+ @Override
+ protected JComponent createCenterPanel() {
+ JBTable table = new JBTable(new MyKeyValueModel());
+ return ToolbarDecorator.createDecorator(table).setRemoveAction(new AnActionButtonRunnable() {
+ @Override
+ public void run(AnActionButton button) {
+ TableUtil.removeSelectedItems(table);
+ table.repaint();
+ }
+ }).createPanel();
+ }
+
+ @Override
+ protected void doOKAction() {
+ PropertiesComponent.getInstance(myProject).setValue(LAST_USED_PROPERTIES_FILE, (String)myPropertiesFile.getSelectedItem());
+ super.doOKAction();
+ }
+
+ private class MyKeyValueModel extends AbstractTableModel implements ItemRemovable {
+ @Override
+ public int getRowCount() {
+ return myKeyValuePairs.size();
+ }
+
+ @Override
+ public String getColumnName(int column) {
+ return column == 0 ? "Key" : "Value";
+ }
+
+ @Override
+ public int getColumnCount() {
+ return 2;
+ }
+
+ @Override
+ public boolean isCellEditable(int rowIndex, int columnIndex) {
+ return columnIndex == 0;
+ }
+
+ @Override
+ public Object getValueAt(int rowIndex, int columnIndex) {
+ ReplacementBean pair = myKeyValuePairs.get(rowIndex);
+ return columnIndex == 0 ? pair.getKey() : pair.getValue();
+ }
+
+ @Override
+ public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
+ if (columnIndex == 0) {
+ ReplacementBean bean = myKeyValuePairs.get(rowIndex);
+ myKeyValuePairs.set(rowIndex, new ReplacementBean((String)aValue, bean.getValue(), bean.getExpression()));
+ }
+ }
+
+ @Override
+ public void removeRow(int idx) {
+ myKeyValuePairs.remove(idx);
+ }
+ }
+ }
+
+ private static class ReplacementBean {
+ private final String myKey;
+ private final String myValue;
+ private final PsiLiteralExpression myExpression;
+
+ private ReplacementBean(String key, String value, PsiLiteralExpression expression) {
+ myKey = key;
+ myValue = value;
+ myExpression = expression;
+ }
+
+ public String getKey() {
+ return myKey;
+ }
+
+ private String getValue() {
+ return myValue;
+ }
+
+ private PsiLiteralExpression getExpression() {
+ return myExpression;
+ }
+ }
+}
diff --git a/plugins/java-i18n/src/com/intellij/codeInspection/i18n/JavaI18nizeQuickFixDialog.java b/plugins/java-i18n/src/com/intellij/codeInspection/i18n/JavaI18nizeQuickFixDialog.java
index c31da2a62f6c..e361b9200f21 100644
--- a/plugins/java-i18n/src/com/intellij/codeInspection/i18n/JavaI18nizeQuickFixDialog.java
+++ b/plugins/java-i18n/src/com/intellij/codeInspection/i18n/JavaI18nizeQuickFixDialog.java
@@ -56,7 +56,7 @@ public class JavaI18nizeQuickFixDialog extends I18nizeQuickFixDialog {
private final boolean myShowPreview;
@NonNls public static final String PROPERTY_KEY_OPTION_KEY = "PROPERTY_KEY";
- @NonNls private static final String RESOURCE_BUNDLE_OPTION_KEY = "RESOURCE_BUNDLE";
+ @NonNls public static final String RESOURCE_BUNDLE_OPTION_KEY = "RESOURCE_BUNDLE";
@NonNls public static final String PROPERTY_VALUE_ATTR = "PROPERTY_VALUE";
public JavaI18nizeQuickFixDialog(@NotNull Project project,
diff --git a/plugins/properties/src/com/intellij/lang/properties/references/I18nizeQuickFixDialog.java b/plugins/properties/src/com/intellij/lang/properties/references/I18nizeQuickFixDialog.java
index ee824b3b468f..62f6657717df 100644
--- a/plugins/properties/src/com/intellij/lang/properties/references/I18nizeQuickFixDialog.java
+++ b/plugins/properties/src/com/intellij/lang/properties/references/I18nizeQuickFixDialog.java
@@ -209,11 +209,13 @@ public class I18nizeQuickFixDialog extends DialogWrapper implements I18nizeQuick
if (myCustomization.suggestedName != null) {
return myCustomization.suggestedName;
}
+ return suggestUniquePropertyKey(value, defaultSuggestPropertyKey(value), getPropertiesFile());
+ }
+ public static String suggestUniquePropertyKey(String value, String defaultKey, PropertiesFile propertiesFile) {
// suggest property key not existing in this file
- String key = defaultSuggestPropertyKey(value);
value = PATTERN.matcher(Normalizer.normalize(value, Normalizer.Form.NFD)).replaceAll("");
- if (key == null) {
+ if (defaultKey == null) {
final StringBuilder result = new StringBuilder();
boolean insertDotBeforeNextWord = false;
for (int i = 0; i < value.length(); i++) {
@@ -241,21 +243,20 @@ public class I18nizeQuickFixDialog extends DialogWrapper implements I18nizeQuick
}
}
}
- key = result.toString();
+ defaultKey = result.toString();
}
- PropertiesFile propertiesFile = getPropertiesFile();
if (propertiesFile != null) {
- if (propertiesFile.findPropertyByKey(key) == null) return key;
+ if (propertiesFile.findPropertyByKey(defaultKey) == null) return defaultKey;
int suffix = 1;
- while (propertiesFile.findPropertyByKey(key + suffix) != null) {
+ while (propertiesFile.findPropertyByKey(defaultKey + suffix) != null) {
suffix++;
}
- return key + suffix;
+ return defaultKey + suffix;
}
else {
- return key;
+ return defaultKey;
}
}