From 29721d30165d0b030b921be589b0fd1ca5caa7c3 Mon Sep 17 00:00:00 2001 From: marhali Date: Wed, 6 Apr 2022 09:34:45 +0200 Subject: [PATCH 01/51] update roadmap --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index c83ee0f..ba600c3 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,8 @@ _For more examples, please refer to the [Examples Directory](https://github.com/ - [X] JSON5 Support - [ ] XML Support - [ ] Mark duplicate translation values +- [ ] Configurable namespace and section separators +- [ ] Define default namespace to use if none was provided See the [open issues](https://github.com/marhali/easy-i18n/issues) for a full list of proposed features (and known issues). From 0f2b8ade4c4b804ca2ece5256a306f57e66925ed Mon Sep 17 00:00:00 2001 From: marhali Date: Thu, 7 Apr 2022 16:10:17 +0200 Subject: [PATCH 02/51] introduce new data structure for translation values --- .../model/translation/Translation.java | 175 ++++++++++++++++++ .../model/translation/variant/ContextMap.java | 11 ++ .../model/translation/variant/LocaleMap.java | 17 ++ .../model/translation/variant/Plural.java | 11 ++ .../model/translation/variant/PluralMap.java | 9 + .../de/marhali/easyi18n/TranslationTest.java | 43 +++++ 6 files changed, 266 insertions(+) create mode 100644 src/main/java/de/marhali/easyi18n/model/translation/Translation.java create mode 100644 src/main/java/de/marhali/easyi18n/model/translation/variant/ContextMap.java create mode 100644 src/main/java/de/marhali/easyi18n/model/translation/variant/LocaleMap.java create mode 100644 src/main/java/de/marhali/easyi18n/model/translation/variant/Plural.java create mode 100644 src/main/java/de/marhali/easyi18n/model/translation/variant/PluralMap.java create mode 100644 src/test/java/de/marhali/easyi18n/TranslationTest.java diff --git a/src/main/java/de/marhali/easyi18n/model/translation/Translation.java b/src/main/java/de/marhali/easyi18n/model/translation/Translation.java new file mode 100644 index 0000000..e5e59cd --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/model/translation/Translation.java @@ -0,0 +1,175 @@ +package de.marhali.easyi18n.model.translation; + +import de.marhali.easyi18n.model.translation.variant.Plural; + +import de.marhali.easyi18n.model.translation.variant.ContextMap; +import de.marhali.easyi18n.model.translation.variant.LocaleMap; +import de.marhali.easyi18n.model.translation.variant.PluralMap; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Represents the set values behind a specific translation. + * Consideration is given to context, pluralization and locale. + *
+ * Data structure can be imagined like a layered map of: context => plural => locale + * + * @author marhali + */ +public class Translation { + + private @Nullable String description; + private @NotNull ContextMap contexts; + private @Nullable Object misc; + + public Translation(@Nullable String description, @NotNull ContextMap contexts, @Nullable Object misc) { + this.description = description; + this.contexts = contexts; + this.misc = misc; + } + + public Translation() { + this(null, new ContextMap(), null); + } + + /** + * Retrieve additional description for this translation + * @return Description + */ + public @Nullable String getDescription() { + return description; + } + + /** + * Override or set description for this translation + * @param description Description + */ + public void setDescription(@Nullable String description) { + this.description = description; + } + + /** + * Retrieve all contexts for this translation + * @return Map of specified contexts + */ + public @NotNull ContextMap getContexts() { + return contexts; + } + + /** + * Check whether a specific context has been set for this translation + * @param context Context to check + * @return True if context has been configured otherwise false + */ + public boolean hasContext(@NotNull String context) { + return contexts.containsKey(context); + } + + /** + * Retrieve all plurals for a specific context. + * @param context Context to apply + * @return Map of specified plurals + */ + public @Nullable PluralMap getPlurals(@NotNull String context) { + return contexts.get(context); + } + + /** + * Retrieve all locale translations for a specific context & pluralization + * @param context Context to apply + * @param plural Pluralization to apply + * @return Map of specified locales + */ + public @Nullable LocaleMap getLocales(@NotNull String context, @NotNull Plural plural) { + return contexts.getOrDefault(context, new PluralMap()).get(plural); + } + + /** + * Retrieve a specific locale translation for a specific context, pluralization and locale + * @param context Context to apply + * @param plural Pluralization to apply + * @param locale Locale to apply + * @return Translated locale value for the specified variant + */ + public @Nullable String getValue(@NotNull String context, @NotNull Plural plural, @NotNull String locale) { + return contexts.getOrDefault(context, new PluralMap()).getOrDefault(plural, new LocaleMap()).get(locale); + } + + /** + * Override or set context map. + * @param contexts New contexts + */ + public void set(@NotNull ContextMap contexts) { + this.contexts = contexts; + } + + /** + * Override or set a specific context + * @param context Context to use + * @param plurals New plurals map + */ + public void set(@NotNull String context, @NotNull PluralMap plurals) { + contexts.put(context, plurals); + } + + /** + * Override or set locales for a specific context & pluralization + * @param context Context to use + * @param plural Pluralization to use + * @param locales New locales map + */ + public void set(@NotNull String context, @NotNull Plural plural, @NotNull LocaleMap locales) { + PluralMap plurals = getPlurals(context); + + if(plurals == null) { + plurals = new PluralMap(); + } + + plurals.put(plural, locales); + set(context, plurals); + } + + /** + * Override or update a specific translation variant + * @param context Context to use + * @param plural Pluralization to use + * @param locale Locale to use + * @param value New value to set + */ + public void set(@NotNull String context, @NotNull Plural plural, @NotNull String locale, @NotNull String value) { + LocaleMap locales = getLocales(context, plural); + + if(locales == null) { + locales = new LocaleMap(); + } + + locales.put(locale, value); + set(context, plural, locales); + } + + /** + * I18n support data + * @return Data + */ + public @Nullable Object getMisc() { + return misc; + } + + /** + * Set or update I18n support data + * @param misc New Data + */ + public void setMisc(@Nullable Object misc) { + this.misc = misc; + } + + @Override + public String toString() { + return "Translation{" + + "description='" + description + '\'' + + ", contexts=" + contexts + + ", misc=" + misc + + '}'; + } +} diff --git a/src/main/java/de/marhali/easyi18n/model/translation/variant/ContextMap.java b/src/main/java/de/marhali/easyi18n/model/translation/variant/ContextMap.java new file mode 100644 index 0000000..0f4e10e --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/model/translation/variant/ContextMap.java @@ -0,0 +1,11 @@ +package de.marhali.easyi18n.model.translation.variant; + +import java.util.HashMap; + +/** + * Maps context with pluralization. + * @author marhali + */ +public class ContextMap extends HashMap { + public static final String DEFAULT = "default"; +} diff --git a/src/main/java/de/marhali/easyi18n/model/translation/variant/LocaleMap.java b/src/main/java/de/marhali/easyi18n/model/translation/variant/LocaleMap.java new file mode 100644 index 0000000..76a7cf4 --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/model/translation/variant/LocaleMap.java @@ -0,0 +1,17 @@ +package de.marhali.easyi18n.model.translation.variant; + +import java.util.HashMap; +import java.util.Map; + +/** + * Maps locale type with specified value + * @author marhali + */ +public class LocaleMap extends HashMap { + + public LocaleMap() {} + + public LocaleMap(Map m) { + super(m); + } +} \ No newline at end of file diff --git a/src/main/java/de/marhali/easyi18n/model/translation/variant/Plural.java b/src/main/java/de/marhali/easyi18n/model/translation/variant/Plural.java new file mode 100644 index 0000000..7de4543 --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/model/translation/variant/Plural.java @@ -0,0 +1,11 @@ +package de.marhali.easyi18n.model.translation.variant; + +/** + * Represents all possible pluralization forms a translation can support. + * @author marhali + */ +public enum Plural { + ZERO, ONE, TWO, FEW, MANY, OTHER; + + public static final Plural DEFAULT = Plural.ONE; +} diff --git a/src/main/java/de/marhali/easyi18n/model/translation/variant/PluralMap.java b/src/main/java/de/marhali/easyi18n/model/translation/variant/PluralMap.java new file mode 100644 index 0000000..a79047f --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/model/translation/variant/PluralMap.java @@ -0,0 +1,9 @@ +package de.marhali.easyi18n.model.translation.variant; + +import java.util.HashMap; + +/** + * Maps pluralization with locale values. + * @author marhali + */ +public class PluralMap extends HashMap {} diff --git a/src/test/java/de/marhali/easyi18n/TranslationTest.java b/src/test/java/de/marhali/easyi18n/TranslationTest.java new file mode 100644 index 0000000..7e8f9ad --- /dev/null +++ b/src/test/java/de/marhali/easyi18n/TranslationTest.java @@ -0,0 +1,43 @@ +package de.marhali.easyi18n; + +import de.marhali.easyi18n.model.translation.variant.Plural; +import de.marhali.easyi18n.model.translation.variant.ContextMap; +import de.marhali.easyi18n.model.translation.variant.LocaleMap; +import de.marhali.easyi18n.model.translation.Translation; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.Map; + +/** + * Unit tests for {@link Translation}. + * @author marhali + */ +public class TranslationTest { + + @Test + public void add() { + Translation value = new Translation(); + value.set(ContextMap.DEFAULT, Plural.DEFAULT, "en", "hello"); + Assert.assertEquals(value.getValue(ContextMap.DEFAULT, Plural.DEFAULT, "en"), "hello"); + } + + @Test + public void override() { + Translation value = new Translation(); + value.set(ContextMap.DEFAULT, Plural.DEFAULT, new LocaleMap(Map.of("en", "hello", "de", "hallo"))); + value.set(ContextMap.DEFAULT, Plural.DEFAULT, "en", "new hello"); + Assert.assertEquals(value.getValue(ContextMap.DEFAULT, Plural.DEFAULT, "en"), "new hello"); + Assert.assertEquals(value.getValue(ContextMap.DEFAULT, Plural.DEFAULT, "de"), "hallo"); + } + + @Test + public void plurals() { + Translation value = new Translation(); + value.set(ContextMap.DEFAULT, Plural.ONE, "en", "boyfriend"); + value.set(ContextMap.DEFAULT, Plural.MANY, "en", "boyfriends"); + Assert.assertEquals(value.getValue(ContextMap.DEFAULT, Plural.ONE, "en"), "boyfriend"); + Assert.assertEquals(value.getValue(ContextMap.DEFAULT, Plural.MANY, "en"), "boyfriends"); + } +} From 09ebf454d4050b46ce116291032e2286b4835083 Mon Sep 17 00:00:00 2001 From: marhali Date: Sat, 9 Apr 2022 12:29:59 +0200 Subject: [PATCH 03/51] properly dispose AsyncFileListener fixes #92 --- CHANGELOG.md | 8 +++++--- src/main/java/de/marhali/easyi18n/DataStore.java | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c2160d1..f133621 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,12 +3,14 @@ # easy-i18n Changelog ## [Unreleased] +### Fixed +- AlreadyDisposedException on FileChangeListener after project has been disposed ## [3.2.0] -### Added -- Support for IntelliJ 2022.1 +### Added +- Support for IntelliJ 2022.1 -### Changed +### Changed - Updated dependencies ## [3.1.0] diff --git a/src/main/java/de/marhali/easyi18n/DataStore.java b/src/main/java/de/marhali/easyi18n/DataStore.java index 0d65c42..4ad6301 100644 --- a/src/main/java/de/marhali/easyi18n/DataStore.java +++ b/src/main/java/de/marhali/easyi18n/DataStore.java @@ -35,7 +35,7 @@ public class DataStore { this.changeListener = new FileChangeListener(project); VirtualFileManager.getInstance().addAsyncFileListener( - this.changeListener, Disposer.newDisposable("EasyI18n")); + this.changeListener, Disposer.newDisposable(project, "EasyI18n")); } public @NotNull TranslationData getData() { From b6fc0c08ffcb73c7fcfa9b31f8fc01f383dd532d Mon Sep 17 00:00:00 2001 From: marhali Date: Sat, 9 Apr 2022 12:33:35 +0200 Subject: [PATCH 04/51] add simple reload action --- src/main/java/de/marhali/easyi18n/InstanceManager.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/de/marhali/easyi18n/InstanceManager.java b/src/main/java/de/marhali/easyi18n/InstanceManager.java index 129f1be..1724260 100644 --- a/src/main/java/de/marhali/easyi18n/InstanceManager.java +++ b/src/main/java/de/marhali/easyi18n/InstanceManager.java @@ -52,6 +52,15 @@ public class InstanceManager { return this.bus; } + /** + * Reloads the plugin instance. Unsaved cached data will be deleted. + * Fetches data from persistence layer and notifies all endpoints via {@link DataBus}. + */ + public void reload() { + store.loadFromPersistenceLayer((success) -> + bus.propagate().onUpdateData(store.getData())); + } + public void processUpdate(TranslationUpdate update) { if(update.isDeletion() || update.isKeyChange()) { // Remove origin translation this.store.getData().setTranslation(update.getOrigin().getKey(), null); From d7f34a35dbcc3e4a6d81a05416c466298a504b72 Mon Sep 17 00:00:00 2001 From: marhali Date: Sat, 9 Apr 2022 12:39:40 +0200 Subject: [PATCH 05/51] rework plugin configuration --- .../easyi18n/settings/ProjectSettings.java | 32 +++ .../settings/ProjectSettingsComponent.java | 212 ++++++++++++++++++ .../ProjectSettingsComponentState.java | 82 +++++++ .../settings/ProjectSettingsConfigurable.java | 59 +++++ .../settings/ProjectSettingsService.java | 47 ++++ .../settings/ProjectSettingsState.java | 156 +++++++++++++ src/main/resources/META-INF/plugin.xml | 21 +- src/main/resources/messages.properties | 51 +++-- 8 files changed, 639 insertions(+), 21 deletions(-) create mode 100644 src/main/java/de/marhali/easyi18n/settings/ProjectSettings.java create mode 100644 src/main/java/de/marhali/easyi18n/settings/ProjectSettingsComponent.java create mode 100644 src/main/java/de/marhali/easyi18n/settings/ProjectSettingsComponentState.java create mode 100644 src/main/java/de/marhali/easyi18n/settings/ProjectSettingsConfigurable.java create mode 100644 src/main/java/de/marhali/easyi18n/settings/ProjectSettingsService.java create mode 100644 src/main/java/de/marhali/easyi18n/settings/ProjectSettingsState.java diff --git a/src/main/java/de/marhali/easyi18n/settings/ProjectSettings.java b/src/main/java/de/marhali/easyi18n/settings/ProjectSettings.java new file mode 100644 index 0000000..1f17de5 --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/settings/ProjectSettings.java @@ -0,0 +1,32 @@ +package de.marhali.easyi18n.settings; + +import de.marhali.easyi18n.io.parser.ParserStrategyType; +import de.marhali.easyi18n.model.FolderStrategyType; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * API to access the project-specific configuration for this plugin. + * @author marhaliu + */ +public interface ProjectSettings { + // Resource Configuration + @Nullable String getLocalesDirectory(); + @NotNull FolderStrategyType getFolderStrategy(); + @NotNull ParserStrategyType getParserStrategy(); + @NotNull String getFilePattern(); + + boolean isSorting(); + + // Editor Configuration + @Nullable String getNamespaceDelimiter(); + @NotNull String getSectionDelimiter(); + @Nullable String getContextDelimiter(); + @Nullable String getPluralDelimiter(); + @Nullable String getDefaultNamespace(); + @NotNull String getPreviewLocale(); + + boolean isNestedKeys(); + boolean isAssistance(); +} diff --git a/src/main/java/de/marhali/easyi18n/settings/ProjectSettingsComponent.java b/src/main/java/de/marhali/easyi18n/settings/ProjectSettingsComponent.java new file mode 100644 index 0000000..95f38e6 --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/settings/ProjectSettingsComponent.java @@ -0,0 +1,212 @@ +package de.marhali.easyi18n.settings; + +import com.intellij.ide.BrowserUtil; +import com.intellij.openapi.fileChooser.FileChooserDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.ComboBox; +import com.intellij.openapi.ui.TextFieldWithBrowseButton; +import com.intellij.ui.JBColor; +import com.intellij.ui.TitledSeparator; +import com.intellij.ui.components.*; +import com.intellij.ui.components.fields.ExtendableTextField; +import com.intellij.util.ui.FormBuilder; + +import de.marhali.easyi18n.io.parser.ArrayMapper; +import de.marhali.easyi18n.io.parser.ParserStrategyType; +import de.marhali.easyi18n.settings.presets.Preset; + +import javax.swing.*; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import java.awt.*; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.util.ResourceBundle; + +/** + * Configuration panel with all possible options for this plugin. + * @author marhali + */ +public class ProjectSettingsComponent extends ProjectSettingsComponentState { + + private final Project project; + private final ResourceBundle bundle; + private final JPanel mainPanel; + + // Data fields are provided by the underlying state class + + public ProjectSettingsComponent(Project project) { + this.project = project; + this.bundle = ResourceBundle.getBundle("messages"); + + this.mainPanel = FormBuilder.createFormBuilder() + .addComponent(new JBLabel(bundle.getString("settings.hint.text"))) + .addComponent(new ActionLink(bundle.getString("settings.hint.action"), + (ActionListener) (var) -> BrowserUtil.browse("https://github.com/marhali/easy-i18n"))) + .addVerticalGap(24) + .addLabeledComponent(bundle.getString("settings.preset.title"), constructPresetField(), 1, false) + .addVerticalGap(12) + .addComponent(new TitledSeparator(bundle.getString("settings.resource.title"))) + .addLabeledComponent(bundle.getString("settings.resource.path.title"), constructLocalesDirectoryField(), 1, false) + .addLabeledComponent(bundle.getString("settings.resource.strategy"), constructFileStrategyPanel(), 1, false) + .addVerticalGap(12) + .addComponent(constructSortingField()) + .addVerticalGap(24) + .addComponent(new TitledSeparator(bundle.getString("settings.editor.title"))) + .addLabeledComponent(bundle.getString("settings.editor.key.title"), constructKeyStrategyPanel(), 1, false) + .addLabeledComponent(bundle.getString("settings.editor.default-namespace.title"), constructDefaultNamespaceField(), 1, false) + .addLabeledComponent(bundle.getString("settings.editor.preview.title"), constructPreviewLocaleField(), 1, false) + .addVerticalGap(12) + .addComponent(constructNestedKeysField()) + .addComponent(constructAssistanceField()) + .addComponentFillVertically(new JPanel(), 0) + .getPanel(); + } + + private JComponent constructPresetField() { + preset = new ComboBox<>(Preset.values()); + preset.setToolTipText(bundle.getString("settings.preset.tooltip")); + preset.setMinimumAndPreferredWidth(196); + preset.addActionListener(e -> setState(preset.getItem().config())); // Listen to selection change + return preset; + } + + private JComponent constructLocalesDirectoryField() { + localesDirectory = new TextFieldWithBrowseButton(); + localesDirectory.setToolTipText(bundle.getString("settings.resource.path.tooltip")); + localesDirectory.addBrowseFolderListener(bundle.getString("settings.resource.path.window"), + bundle.getString("settings.resource.path.tooltip"), project, + new FileChooserDescriptor(false, true, + false, false, false, false)); + + // Listen to value change + localesDirectory.getTextField().getDocument().addDocumentListener(new DocumentListener() { + @Override + public void insertUpdate(DocumentEvent e) { + validateLocalesDirectory(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + validateLocalesDirectory(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + validateLocalesDirectory(); + } + }); + + validateLocalesDirectory(); + return localesDirectory; + } + + private void validateLocalesDirectory() { + // Paint red border to indicate missing value + localesDirectory.setBorder(localesDirectory.getText().isEmpty() + ? BorderFactory.createLineBorder(JBColor.red) : null); + } + + private JPanel constructFileStrategyPanel() { + JPanel panel = new JBPanel<>(new GridBagLayout()); + GridBagConstraints constraints = new GridBagConstraints(); + + /* folder strategy */ + folderStrategy = new ComboBox<>(bundle.getString("settings.resource.folder.items").split(ArrayMapper.SPLITERATOR_REGEX)); + folderStrategy.setToolTipText(bundle.getString("settings.resource.folder.tooltip")); + folderStrategy.setMinimumAndPreferredWidth(256); + constraints.fill = GridBagConstraints.HORIZONTAL; + constraints.gridx = 0; + constraints.gridy = 0; + panel.add(folderStrategy, constraints); + + /* parser strategy */ + parserStrategy = new ComboBox<>(bundle.getString("settings.resource.parser.items").split(ArrayMapper.SPLITERATOR_REGEX)); + parserStrategy.setToolTipText(bundle.getString("settings.resource.parser.tooltip")); + parserStrategy.addItemListener(handleParserChange()); + parserStrategy.setMinimumAndPreferredWidth(128); + constraints.fill = GridBagConstraints.HORIZONTAL; + constraints.gridx = 1; + constraints.gridy = 0; + panel.add(parserStrategy, constraints); + + /* file pattern strategy */ + filePattern = new JBTextField(); + filePattern.setToolTipText(bundle.getString("settings.resource.file-pattern.tooltip")); + constraints.fill = GridBagConstraints.HORIZONTAL; + constraints.gridx = 2; + constraints.gridy = 0; + constraints.weightx = 1; + panel.add(filePattern, constraints); + + return panel; + } + + private JComponent constructSortingField() { + sorting = new JBCheckBox(bundle.getString("settings.resource.sorting.title")); + sorting.setToolTipText(bundle.getString("settings.resource.sorting.tooltip")); + return sorting; + } + + private JPanel constructKeyStrategyPanel() { + JPanel panel = new JBPanel<>(new FlowLayout(FlowLayout.LEFT)); + + panel.add(new JBLabel(bundle.getString("settings.editor.key.namespace.title"))); + panel.add(namespaceDelimiter = createDelimiterField(bundle.getString("settings.editor.key.namespace.tooltip"))); + panel.add(new JBLabel(bundle.getString("settings.editor.key.section.title"))); + panel.add(sectionDelimiter = createDelimiterField(bundle.getString("settings.editor.key.section.tooltip"))); + panel.add(new JBLabel(bundle.getString("settings.editor.key.node.title"))); + panel.add(contextDelimiter = createDelimiterField(bundle.getString("settings.editor.key.context.tooltip"))); + panel.add(new JBLabel(bundle.getString("settings.editor.key.context.title"))); + panel.add(pluralDelimiter = createDelimiterField(bundle.getString("settings.editor.key.plural.tooltip"))); + panel.add(new JBLabel(bundle.getString("settings.editor.key.plural.title"))); + + return panel; + } + + private JTextField createDelimiterField(String tooltip) { + JBTextField field = new JBTextField(); + field.setHorizontalAlignment(JTextField.CENTER); + field.setToolTipText(tooltip); + return field; + } + + private JComponent constructDefaultNamespaceField() { + defaultNamespace = new ExtendableTextField(20); + defaultNamespace.setToolTipText(bundle.getString("settings.editor.default-namespace.tooltip")); + return defaultNamespace; + } + + private JComponent constructPreviewLocaleField() { + previewLocale = new ExtendableTextField(12); + previewLocale.setToolTipText(bundle.getString("settings.editor.preview.tooltip")); + return previewLocale; + } + + private JComponent constructNestedKeysField() { + nestedKeys = new JBCheckBox(bundle.getString("settings.editor.key.nesting.title")); + nestedKeys.setToolTipText(bundle.getString("settings.editor.key.nesting.tooltip")); + return nestedKeys; + } + + private JComponent constructAssistanceField() { + assistance = new JBCheckBox(bundle.getString("settings.editor.assistance.title")); + assistance.setToolTipText(bundle.getString("settings.editor.assistance.tooltip")); + return assistance; + } + + private ItemListener handleParserChange() { + return e -> { + if(e.getStateChange() == ItemEvent.SELECTED) { + // Automatically suggest file pattern option on parser change + ParserStrategyType newStrategy = ParserStrategyType.fromIndex(parserStrategy.getSelectedIndex()); + filePattern.setText(newStrategy.getExampleFilePattern()); + } + }; + } + + public JPanel getMainPanel() { + return mainPanel; + } +} diff --git a/src/main/java/de/marhali/easyi18n/settings/ProjectSettingsComponentState.java b/src/main/java/de/marhali/easyi18n/settings/ProjectSettingsComponentState.java new file mode 100644 index 0000000..3091472 --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/settings/ProjectSettingsComponentState.java @@ -0,0 +1,82 @@ +package de.marhali.easyi18n.settings; + +import com.intellij.openapi.ui.ComboBox; +import com.intellij.openapi.ui.TextFieldWithBrowseButton; + +import de.marhali.easyi18n.io.parser.ParserStrategyType; +import de.marhali.easyi18n.model.FolderStrategyType; +import de.marhali.easyi18n.settings.presets.Preset; + +import javax.swing.*; + +/** + * Mandatory for state management for the project settings component. + * @author marhali + */ +public class ProjectSettingsComponentState { + + protected ComboBox preset; + + // Resource Configuration + protected TextFieldWithBrowseButton localesDirectory; + protected ComboBox folderStrategy; + protected ComboBox parserStrategy; + protected JTextField filePattern; + + protected JCheckBox sorting; + + // Editor configuration + protected JTextField namespaceDelimiter; + protected JTextField sectionDelimiter; + protected JTextField contextDelimiter; + protected JTextField pluralDelimiter; + protected JTextField defaultNamespace; + protected JTextField previewLocale; + + protected JCheckBox nestedKeys; + protected JCheckBox assistance; + + protected ProjectSettingsState getState() { + // Every field needs to provide its state + ProjectSettingsState state = new ProjectSettingsState(); + + state.setLocalesDirectory(localesDirectory.getText()); + state.setFolderStrategy(FolderStrategyType.fromIndex(folderStrategy.getSelectedIndex())); + state.setParserStrategy(ParserStrategyType.fromIndex(parserStrategy.getSelectedIndex())); + state.setFilePattern(filePattern.getText()); + + state.setSorting(sorting.isSelected()); + + state.setNamespaceDelimiter(namespaceDelimiter.getText()); + state.setSectionDelimiter(sectionDelimiter.getText()); + state.setContextDelimiter(contextDelimiter.getText()); + state.setPluralDelimiter(pluralDelimiter.getText()); + state.setDefaultNamespace(defaultNamespace.getText()); + state.setPreviewLocale(previewLocale.getText()); + + state.setNestedKeys(nestedKeys.isSelected()); + state.setAssistance(assistance.isSelected()); + + return state; + } + + protected void setState(ProjectSettings state) { + // Update every field with the new state + localesDirectory.setText(state.getLocalesDirectory()); + folderStrategy.setSelectedIndex(state.getFolderStrategy().toIndex()); + parserStrategy.setSelectedIndex((state.getParserStrategy().toIndex())); + filePattern.setText(state.getFilePattern()); + + sorting.setSelected(state.isSorting()); + + namespaceDelimiter.setText(state.getNamespaceDelimiter()); + sectionDelimiter.setText(state.getSectionDelimiter()); + contextDelimiter.setText(state.getContextDelimiter()); + pluralDelimiter.setText(state.getPluralDelimiter()); + defaultNamespace.setText(state.getDefaultNamespace()); + previewLocale.setText(state.getPreviewLocale()); + + nestedKeys.setSelected(state.isNestedKeys()); + assistance.setSelected(state.isAssistance()); + } +} diff --git a/src/main/java/de/marhali/easyi18n/settings/ProjectSettingsConfigurable.java b/src/main/java/de/marhali/easyi18n/settings/ProjectSettingsConfigurable.java new file mode 100644 index 0000000..dcb07b6 --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/settings/ProjectSettingsConfigurable.java @@ -0,0 +1,59 @@ +package de.marhali.easyi18n.settings; + +import com.intellij.openapi.options.Configurable; +import com.intellij.openapi.project.Project; + +import de.marhali.easyi18n.InstanceManager; + +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; + +/** + * IDE settings panel for this plugin + * @author marhali + */ +public class ProjectSettingsConfigurable implements Configurable { + + private final Project project; + + private ProjectSettingsComponent component; + + public ProjectSettingsConfigurable(Project project) { + this.project = project; + } + + @Override + public String getDisplayName() { + return "Easy I18n"; + } + + @Override + public @Nullable JComponent createComponent() { + component = new ProjectSettingsComponent(project); + component.setState(ProjectSettingsService.get(project).getState()); + return component.getMainPanel(); + } + + @Override + public boolean isModified() { + ProjectSettingsState originState = ProjectSettingsService.get(project).getState(); + return !originState.equals(component.getState()); + } + + @Override + public void apply() { + ProjectSettingsService.get(project).setState(component.getState()); + InstanceManager.get(project).reload(); + } + + @Override + public void reset() { + component.setState(ProjectSettingsService.get(project).getState()); + } + + @Override + public void disposeUIResources() { + component = null; + } +} diff --git a/src/main/java/de/marhali/easyi18n/settings/ProjectSettingsService.java b/src/main/java/de/marhali/easyi18n/settings/ProjectSettingsService.java new file mode 100644 index 0000000..1cc0538 --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/settings/ProjectSettingsService.java @@ -0,0 +1,47 @@ +package de.marhali.easyi18n.settings; + +import com.intellij.openapi.components.PersistentStateComponent; +import com.intellij.openapi.components.State; +import com.intellij.openapi.components.Storage; +import com.intellij.openapi.project.Project; + +import org.jetbrains.annotations.NotNull; + +/** + * Persistent storage for project-specific settings. + * @author marhali + */ +@State( + name = "ProjectSettingsService", + storages = @Storage("easy-i18n.xml") +) +public class ProjectSettingsService implements PersistentStateComponent { + + public static @NotNull ProjectSettingsService get(@NotNull Project project) { + return project.getService(ProjectSettingsService.class); + } + + private ProjectSettingsState state; + + public ProjectSettingsService() { + this.state = new ProjectSettingsState(); + } + + /** + * Sets the provided configuration and invalidates the merged state. + * @param state New configuration + */ + protected void setState(@NotNull ProjectSettingsState state) { + this.state = state; + } + + @Override + public @NotNull ProjectSettingsState getState() { + return state; + } + + @Override + public void loadState(@NotNull ProjectSettingsState state) { + this.state = state; + } +} diff --git a/src/main/java/de/marhali/easyi18n/settings/ProjectSettingsState.java b/src/main/java/de/marhali/easyi18n/settings/ProjectSettingsState.java new file mode 100644 index 0000000..3ec02df --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/settings/ProjectSettingsState.java @@ -0,0 +1,156 @@ +package de.marhali.easyi18n.settings; + +import de.marhali.easyi18n.io.parser.ParserStrategyType; +import de.marhali.easyi18n.model.FolderStrategyType; +import de.marhali.easyi18n.settings.presets.DefaultPreset; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Represents the project-specific configuration of this plugin. + * @author marhali + */ +public class ProjectSettingsState implements ProjectSettings { + + private static final ProjectSettings defaults = new DefaultPreset(); + + // Resource Configuration + private String localesDirectory; + private FolderStrategyType folderStrategy; + private ParserStrategyType parserStrategy; + private String filePattern; + + private Boolean sorting; + + // Editor configuration + private String namespaceDelimiter; + private String sectionDelimiter; + private String contextDelimiter; + private String pluralDelimiter; + private String defaultNamespace; + private String previewLocale; + + private Boolean nestedKeys; + private Boolean assistance; + + public ProjectSettingsState() {} + + @Override + public @Nullable String getLocalesDirectory() { + return localesDirectory != null ? localesDirectory : defaults.getLocalesDirectory(); + } + + @Override + public @NotNull FolderStrategyType getFolderStrategy() { + return folderStrategy != null ? folderStrategy : defaults.getFolderStrategy(); + } + + @Override + public @NotNull ParserStrategyType getParserStrategy() { + return parserStrategy != null ? parserStrategy : defaults.getParserStrategy(); + } + + @Override + public @NotNull String getFilePattern() { + return filePattern != null ? filePattern : defaults.getFilePattern(); + } + + @Override + public boolean isSorting() { + return sorting != null ? sorting : defaults.isSorting(); + } + + @Override + public @Nullable String getNamespaceDelimiter() { + return namespaceDelimiter != null ? namespaceDelimiter : defaults.getNamespaceDelimiter(); + } + + @Override + public @NotNull String getSectionDelimiter() { + return sectionDelimiter != null ? sectionDelimiter : defaults.getSectionDelimiter(); + } + + @Override + public @Nullable String getContextDelimiter() { + return contextDelimiter != null ? contextDelimiter : defaults.getContextDelimiter(); + } + + @Override + public @Nullable String getPluralDelimiter() { + return pluralDelimiter != null ? pluralDelimiter : defaults.getPluralDelimiter(); + } + + @Nullable + @Override + public String getDefaultNamespace() { + return defaultNamespace; + } + + @Override + public @NotNull String getPreviewLocale() { + return previewLocale != null ? previewLocale : defaults.getPreviewLocale(); + } + + @Override + public boolean isNestedKeys() { + return nestedKeys != null ? nestedKeys : defaults.isNestedKeys(); + } + + @Override + public boolean isAssistance() { + return assistance != null ? assistance : defaults.isAssistance(); + } + + public void setLocalesDirectory(String localesDirectory) { + this.localesDirectory = localesDirectory; + } + + public void setFolderStrategy(FolderStrategyType folderStrategy) { + this.folderStrategy = folderStrategy; + } + + public void setParserStrategy(ParserStrategyType parserStrategy) { + this.parserStrategy = parserStrategy; + } + + public void setFilePattern(String filePattern) { + this.filePattern = filePattern; + } + + public void setSorting(Boolean sorting) { + this.sorting = sorting; + } + + public void setNamespaceDelimiter(String namespaceDelimiter) { + this.namespaceDelimiter = namespaceDelimiter; + } + + public void setSectionDelimiter(String sectionDelimiter) { + this.sectionDelimiter = sectionDelimiter; + } + + public void setContextDelimiter(String contextDelimiter) { + this.contextDelimiter = contextDelimiter; + } + + public void setPluralDelimiter(String pluralDelimiter) { + this.pluralDelimiter = pluralDelimiter; + } + + public void setDefaultNamespace(String defaultNamespace) { + this.defaultNamespace = defaultNamespace; + } + + public void setPreviewLocale(String previewLocale) { + this.previewLocale = previewLocale; + } + + public void setNestedKeys(Boolean nestedKeys) { + this.nestedKeys = nestedKeys; + } + + public void setAssistance(Boolean assistance) { + this.assistance = assistance; + } +} diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 4ff888e..a0c3ee7 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -24,21 +24,26 @@ - + - + + + + implementationClass="de.marhali.easyi18n.editor.generic.GenericKeyCompletionContributor"/> + implementationClass="de.marhali.easyi18n.editor.generic.GenericKeyAnnotator"/> + implementation="de.marhali.easyi18n.editor.generic.GenericKeyReferenceContributor"/> - - - + + + \ No newline at end of file diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties index ed38b92..2c4620c 100644 --- a/src/main/resources/messages.properties +++ b/src/main/resources/messages.properties @@ -12,19 +12,44 @@ action.search=Search... action.delete=Delete translation.key=Key translation.locales=Locales -settings.path.title=Locales Directory -settings.path.text=Locales directory -settings.strategy.title=Translation file structure -settings.strategy.folder=Single Directory;Modularized: Locale / Namespace;Modularized: Namespace / Locale -settings.strategy.folder.tooltip=What is the folder structure of your translation files? -settings.strategy.parser=JSON;JSON5;YAML;YML;Properties;ARB -settings.strategy.parser.tooltip=Which file parser should be used to process your translation files? -settings.strategy.file-pattern.tooltip=Defines a wildcard matcher to filter relevant translation files. For example *.json, *.???. -settings.preview=Preview locale -settings.path.prefix=Path prefix -settings.keys.sort=Sort translation keys alphabetically -settings.keys.nested=Escape delimiter character within a section layer. -settings.editor.assistance=I18n key completion, annotation and reference inside editor +# Settings +settings.hint.text=Project-specific configuration for using your translation files. For an easy start, you can use one of the existing presets. +settings.hint.action=Fore more information, see the documentation +settings.preset.title=Preset +settings.preset.tooltip=Choose a configuration template that best fits your project. After that you can make further changes. +# Resource Configuration +settings.resource.title=Resource Configuration +settings.resource.path.window=Locales Directory +settings.resource.path.title=Locales directory +settings.resource.path.tooltip=Define the folder which contains all translation files. For nested folders, use the top folder. +settings.resource.strategy=File structure +settings.resource.folder.items=Single Directory;Modularized: Locale / Namespace;Modularized: Namespace / Locale +settings.resource.folder.tooltip=What is the folder structure of your translation files? +settings.resource.parser.items=JSON;JSON5;YAML;YML;Properties;ARB +settings.resource.parser.tooltip=Which file parser should be used to process your translation files? +settings.resource.file-pattern.tooltip=Defines a wildcard matcher to filter relevant translation files. For example *.json, *.??? or *.*. +settings.resource.sorting.title=Sort translation keys alphabetically +settings.resource.sorting.tooltip=Sorts all translation keys alphabetically. If disabled, the original key-order in the files is kept. +# Editor Configuration +settings.editor.title=Editor Configuration +settings.editor.key.title=Key delimiters +settings.editor.key.namespace.title=[namespace] +settings.editor.key.namespace.tooltip=Sets the separator used between namespace and key path. +settings.editor.key.section.title=[section] +settings.editor.key.section.tooltip=Sets the separator used between section nodes of the key path. +settings.editor.key.node.title=[node] +settings.editor.key.context.title=[context] +settings.editor.key.context.tooltip=Sets the separator used to define context-specific variants of a translation. +settings.editor.key.plural.title=[pluralization] +settings.editor.key.plural.tooltip=Sets the separator used to define different pluralization's of a translation. +settings.editor.default-namespace.title=Default namespace +settings.editor.default-namespace.tooltip=Specifies the namespace to use by default if none is specified in a translation key. The field can be left blank to ignore this feature. +settings.editor.preview.title=Preview locale +settings.editor.preview.tooltip=Defines the language to be displayed in editor previews. +settings.editor.key.nesting.title=Escape section delimiter within a section layer +settings.editor.key.nesting.tooltip=Escapes the section delimiter within a section to properly reconstruct nested key sections. Disable this feature if nested key sections are the exception rather than the rule in your translation file. +settings.editor.assistance.title=Editor code assistance for translations +settings.editor.assistance.tooltip=Activates editor support to reference, auto-complete and folding of existing translations. error.io=An error occurred while processing translation files. \n\ Config: {0} => {1} ({2}) \n\ Path: {3} \n\ From ba221786c1e997793b981fa1d28b460e1026ad73 Mon Sep 17 00:00:00 2001 From: marhali Date: Sat, 9 Apr 2022 12:40:00 +0200 Subject: [PATCH 06/51] deprecate old configuration --- .../java/de/marhali/easyi18n/action/SettingsAction.java | 7 +++++-- .../java/de/marhali/easyi18n/dialog/SettingsDialog.java | 1 + src/main/java/de/marhali/easyi18n/model/SettingsState.java | 1 + .../java/de/marhali/easyi18n/service/SettingsService.java | 5 +++++ 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/java/de/marhali/easyi18n/action/SettingsAction.java b/src/main/java/de/marhali/easyi18n/action/SettingsAction.java index 42feefa..fabd792 100644 --- a/src/main/java/de/marhali/easyi18n/action/SettingsAction.java +++ b/src/main/java/de/marhali/easyi18n/action/SettingsAction.java @@ -3,7 +3,10 @@ package de.marhali.easyi18n.action; import com.intellij.icons.AllIcons; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; -import de.marhali.easyi18n.dialog.SettingsDialog; +import com.intellij.openapi.options.ShowSettingsUtil; + +import de.marhali.easyi18n.settings.ProjectSettingsConfigurable; + import org.jetbrains.annotations.NotNull; import java.util.ResourceBundle; @@ -21,6 +24,6 @@ public class SettingsAction extends AnAction { @Override public void actionPerformed(@NotNull AnActionEvent e) { - new SettingsDialog(e.getProject()).showAndHandle(); + ShowSettingsUtil.getInstance().showSettingsDialog(e.getProject(), ProjectSettingsConfigurable.class); } } \ No newline at end of file diff --git a/src/main/java/de/marhali/easyi18n/dialog/SettingsDialog.java b/src/main/java/de/marhali/easyi18n/dialog/SettingsDialog.java index b45ca2f..4fca692 100644 --- a/src/main/java/de/marhali/easyi18n/dialog/SettingsDialog.java +++ b/src/main/java/de/marhali/easyi18n/dialog/SettingsDialog.java @@ -28,6 +28,7 @@ import java.util.ResourceBundle; * Plugin configuration dialog. * @author marhali */ +@Deprecated public class SettingsDialog { private final Project project; diff --git a/src/main/java/de/marhali/easyi18n/model/SettingsState.java b/src/main/java/de/marhali/easyi18n/model/SettingsState.java index dc6beb4..4e5c810 100644 --- a/src/main/java/de/marhali/easyi18n/model/SettingsState.java +++ b/src/main/java/de/marhali/easyi18n/model/SettingsState.java @@ -8,6 +8,7 @@ import org.jetbrains.annotations.Nullable; * Represents the persistent settings which can be configured. * @author marhali */ +@Deprecated public class SettingsState { public static final String DEFAULT_PREVIEW_LOCALE = "en"; diff --git a/src/main/java/de/marhali/easyi18n/service/SettingsService.java b/src/main/java/de/marhali/easyi18n/service/SettingsService.java index 59f81ad..9460fa8 100644 --- a/src/main/java/de/marhali/easyi18n/service/SettingsService.java +++ b/src/main/java/de/marhali/easyi18n/service/SettingsService.java @@ -13,6 +13,7 @@ import org.jetbrains.annotations.NotNull; * @author marhali */ @State(name = "EasyI18nSettings") +@Deprecated public class SettingsService implements PersistentStateComponent { public static SettingsService getInstance(Project project) { @@ -30,6 +31,10 @@ public class SettingsService implements PersistentStateComponent return state; } + public void setState(SettingsState state) { + this.state = state; + } + @Override public void loadState(@NotNull SettingsState state) { this.state = state; From 1161e324e3d32c8a46fdf20f692d913e534268a3 Mon Sep 17 00:00:00 2001 From: marhali Date: Sat, 9 Apr 2022 12:41:50 +0200 Subject: [PATCH 07/51] use new simple reload action --- src/main/java/de/marhali/easyi18n/action/ReloadAction.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/de/marhali/easyi18n/action/ReloadAction.java b/src/main/java/de/marhali/easyi18n/action/ReloadAction.java index 323ba44..5a01985 100644 --- a/src/main/java/de/marhali/easyi18n/action/ReloadAction.java +++ b/src/main/java/de/marhali/easyi18n/action/ReloadAction.java @@ -8,6 +8,7 @@ import de.marhali.easyi18n.InstanceManager; import org.jetbrains.annotations.NotNull; +import java.util.Objects; import java.util.ResourceBundle; /** @@ -23,9 +24,6 @@ public class ReloadAction extends AnAction { @Override public void actionPerformed(@NotNull AnActionEvent e) { - InstanceManager manager = InstanceManager.get(e.getProject()); - manager.store().loadFromPersistenceLayer((success) -> { - manager.bus().propagate().onUpdateData(manager.store().getData()); - }); + InstanceManager.get(Objects.requireNonNull(e.getProject())).reload(); } } \ No newline at end of file From 31ad2e346436f88d04158700b587e8a499e05290 Mon Sep 17 00:00:00 2001 From: marhali Date: Sat, 9 Apr 2022 13:00:18 +0200 Subject: [PATCH 08/51] activate new configuration service --- .../java/de/marhali/easyi18n/DataStore.java | 12 +- .../easyi18n/dialog/SettingsDialog.java | 174 ------------------ .../marhali/easyi18n/editor/KeyAnnotator.java | 12 +- .../editor/KeyCompletionProvider.java | 10 +- .../editor/generic/GenericFoldingBuilder.java | 6 +- .../GenericKeyReferenceContributor.java | 4 +- .../kotlin/KotlinKeyReferenceContributor.java | 4 +- .../de/marhali/easyi18n/io/IOHandler.java | 15 +- .../easyi18n/io/folder/FolderStrategy.java | 6 +- .../folder/ModularLocaleFolderStrategy.java | 4 +- .../ModularNamespaceFolderStrategy.java | 4 +- .../io/folder/SingleFolderStrategy.java | 4 +- .../easyi18n/io/parser/ParserStrategy.java | 7 +- .../io/parser/json/JsonParserStrategy.java | 5 +- .../io/parser/json5/Json5ParserStrategy.java | 4 +- .../properties/PropertiesParserStrategy.java | 8 +- .../io/parser/yaml/YamlParserStrategy.java | 4 +- .../easyi18n/model/KeyPathConverter.java | 4 +- .../marhali/easyi18n/model/SettingsState.java | 106 ----------- .../easyi18n/service/SettingsService.java | 42 ----- .../de/marhali/easyi18n/tabs/TreeView.java | 4 +- .../easyi18n/tabs/mapper/TreeModelMapper.java | 9 +- .../easyi18n/util/NotificationHelper.java | 6 +- 23 files changed, 70 insertions(+), 384 deletions(-) delete mode 100644 src/main/java/de/marhali/easyi18n/dialog/SettingsDialog.java delete mode 100644 src/main/java/de/marhali/easyi18n/model/SettingsState.java delete mode 100644 src/main/java/de/marhali/easyi18n/service/SettingsService.java diff --git a/src/main/java/de/marhali/easyi18n/DataStore.java b/src/main/java/de/marhali/easyi18n/DataStore.java index 4ad6301..4b60c9f 100644 --- a/src/main/java/de/marhali/easyi18n/DataStore.java +++ b/src/main/java/de/marhali/easyi18n/DataStore.java @@ -7,10 +7,10 @@ import com.intellij.openapi.vfs.*; import de.marhali.easyi18n.exception.EmptyLocalesDirException; import de.marhali.easyi18n.io.IOHandler; -import de.marhali.easyi18n.model.SettingsState; import de.marhali.easyi18n.model.TranslationData; import de.marhali.easyi18n.service.FileChangeListener; -import de.marhali.easyi18n.service.SettingsService; +import de.marhali.easyi18n.settings.ProjectSettings; +import de.marhali.easyi18n.settings.ProjectSettingsService; import de.marhali.easyi18n.util.NotificationHelper; import org.jetbrains.annotations.NotNull; @@ -48,18 +48,18 @@ public class DataStore { * @param successResult Consumer will inform if operation was successful */ public void loadFromPersistenceLayer(@NotNull Consumer successResult) { - SettingsState settings = SettingsService.getInstance(this.project).getState(); + ProjectSettings settings = ProjectSettingsService.get(project).getState(); ApplicationManager.getApplication().saveAll(); // Save opened files (required if new locales were added) ApplicationManager.getApplication().runReadAction(() -> { try { this.data = new IOHandler(settings).read(); - this.changeListener.updateLocalesPath(settings.getLocalesPath()); + this.changeListener.updateLocalesPath(settings.getLocalesDirectory()); successResult.accept(true); } catch (Exception ex) { - this.data = new TranslationData(settings.isSortKeys()); + this.data = new TranslationData(settings.isSorting()); successResult.accept(false); if(ex instanceof EmptyLocalesDirException) { @@ -76,7 +76,7 @@ public class DataStore { * @param successResult Consumer will inform if operation was successful */ public void saveToPersistenceLayer(@NotNull Consumer successResult) { - SettingsState settings = SettingsService.getInstance(this.project).getState(); + ProjectSettings settings = ProjectSettingsService.get(project).getState(); ApplicationManager.getApplication().runWriteAction(() -> { try { diff --git a/src/main/java/de/marhali/easyi18n/dialog/SettingsDialog.java b/src/main/java/de/marhali/easyi18n/dialog/SettingsDialog.java deleted file mode 100644 index 4fca692..0000000 --- a/src/main/java/de/marhali/easyi18n/dialog/SettingsDialog.java +++ /dev/null @@ -1,174 +0,0 @@ -package de.marhali.easyi18n.dialog; - -import com.intellij.openapi.fileChooser.FileChooserDescriptor; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.ComboBox; -import com.intellij.openapi.ui.DialogBuilder; -import com.intellij.openapi.ui.DialogWrapper; -import com.intellij.openapi.ui.TextFieldWithBrowseButton; -import com.intellij.ui.components.JBCheckBox; -import com.intellij.ui.components.JBLabel; -import com.intellij.ui.components.JBPanel; -import com.intellij.ui.components.JBTextField; - -import de.marhali.easyi18n.InstanceManager; -import de.marhali.easyi18n.io.parser.ArrayMapper; -import de.marhali.easyi18n.model.FolderStrategyType; -import de.marhali.easyi18n.model.SettingsState; -import de.marhali.easyi18n.io.parser.ParserStrategyType; -import de.marhali.easyi18n.service.SettingsService; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.ItemEvent; -import java.awt.event.ItemListener; -import java.util.ResourceBundle; - -/** - * Plugin configuration dialog. - * @author marhali - */ -@Deprecated -public class SettingsDialog { - - private final Project project; - - private TextFieldWithBrowseButton pathText; - private ComboBox folderStrategyComboBox; - private ComboBox parserStrategyComboBox; - private JBTextField filePatternText; - private JBTextField previewLocaleText; - private JBTextField pathPrefixText; - private JBCheckBox sortKeysCheckbox; - private JBCheckBox nestedKeysCheckbox; - private JBCheckBox codeAssistanceCheckbox; - - public SettingsDialog(Project project) { - this.project = project; - } - - public void showAndHandle() { - SettingsState state = SettingsService.getInstance(project).getState(); - - if(prepare(state).show() == DialogWrapper.OK_EXIT_CODE) { // Save changes - state.setLocalesPath(pathText.getText()); - state.setFolderStrategy(FolderStrategyType.fromIndex(folderStrategyComboBox.getSelectedIndex())); - state.setParserStrategy(ParserStrategyType.fromIndex(parserStrategyComboBox.getSelectedIndex())); - state.setFilePattern(filePatternText.getText()); - state.setPreviewLocale(previewLocaleText.getText()); - state.setPathPrefix(pathPrefixText.getText()); - state.setSortKeys(sortKeysCheckbox.isSelected()); - state.setNestedKeys(nestedKeysCheckbox.isSelected()); - state.setCodeAssistance(codeAssistanceCheckbox.isSelected()); - - // Reload instance - InstanceManager manager = InstanceManager.get(project); - manager.store().loadFromPersistenceLayer((success) -> - manager.bus().propagate().onUpdateData(manager.store().getData())); - } - } - - private DialogBuilder prepare(SettingsState state) { - ResourceBundle bundle = ResourceBundle.getBundle("messages"); - JPanel rootPanel = new JPanel(new GridLayout(0, 1, 2, 2)); - - /* path */ - JBLabel pathLabel = new JBLabel(bundle.getString("settings.path.text")); - pathText = new TextFieldWithBrowseButton(new JTextField(state.getLocalesPath())); - - pathLabel.setLabelFor(pathText); - pathText.addBrowseFolderListener(bundle.getString("settings.path.title"), null, project, new FileChooserDescriptor( - false, true, false, false, false, false)); - - rootPanel.add(pathLabel); - rootPanel.add(pathText); - - JBLabel strategyLabel = new JBLabel(bundle.getString("settings.strategy.title")); - rootPanel.add(strategyLabel); - - JPanel strategyPanel = new JBPanel<>(new GridBagLayout()); - rootPanel.add(strategyPanel); - GridBagConstraints constraints = new GridBagConstraints(); - - /* folder strategy */ - folderStrategyComboBox = new ComboBox<>(bundle.getString("settings.strategy.folder").split(ArrayMapper.SPLITERATOR_REGEX)); - folderStrategyComboBox.setSelectedIndex(state.getFolderStrategy().toIndex()); - folderStrategyComboBox.setToolTipText(bundle.getString("settings.strategy.folder.tooltip")); - folderStrategyComboBox.setMinimumAndPreferredWidth(256); - constraints.fill = GridBagConstraints.HORIZONTAL; - constraints.gridx = 0; - constraints.gridy = 0; - strategyPanel.add(folderStrategyComboBox, constraints); - - /* parser strategy */ - parserStrategyComboBox = new ComboBox<>(bundle.getString("settings.strategy.parser").split(ArrayMapper.SPLITERATOR_REGEX)); - parserStrategyComboBox.setSelectedIndex(state.getParserStrategy().toIndex()); - parserStrategyComboBox.setToolTipText(bundle.getString("settings.strategy.parser.tooltip")); - parserStrategyComboBox.addItemListener(handleParserChange()); - constraints.fill = GridBagConstraints.HORIZONTAL; - constraints.gridx = 1; - constraints.gridy = 0; - strategyPanel.add(parserStrategyComboBox, constraints); - - /* file pattern strategy */ - filePatternText = new JBTextField(state.getFilePattern()); - filePatternText.setToolTipText(bundle.getString("settings.strategy.file-pattern.tooltip")); - constraints.fill = GridBagConstraints.HORIZONTAL; - constraints.gridx = 2; - constraints.gridy = 0; - constraints.weightx = 1; - strategyPanel.add(filePatternText, constraints); - - /* preview locale */ - JBLabel previewLocaleLabel = new JBLabel(bundle.getString("settings.preview")); - previewLocaleText = new JBTextField(state.getPreviewLocale()); - previewLocaleLabel.setLabelFor(previewLocaleText); - - rootPanel.add(previewLocaleLabel); - rootPanel.add(previewLocaleText); - - /* path prefix */ - JBLabel pathPrefixLabel = new JBLabel(bundle.getString("settings.path.prefix")); - pathPrefixText = new JBTextField(state.getPathPrefix()); - - rootPanel.add(pathPrefixLabel); - rootPanel.add(pathPrefixText); - - /* sort keys */ - sortKeysCheckbox = new JBCheckBox(bundle.getString("settings.keys.sort")); - sortKeysCheckbox.setSelected(state.isSortKeys()); - - rootPanel.add(sortKeysCheckbox); - - /* nested keys */ - nestedKeysCheckbox = new JBCheckBox(bundle.getString("settings.keys.nested")); - nestedKeysCheckbox.setSelected(state.isNestedKeys()); - - rootPanel.add(nestedKeysCheckbox); - - /* code assistance */ - codeAssistanceCheckbox = new JBCheckBox(bundle.getString("settings.editor.assistance")); - codeAssistanceCheckbox.setSelected(state.isCodeAssistance()); - - rootPanel.add(codeAssistanceCheckbox); - - DialogBuilder builder = new DialogBuilder(); - builder.setTitle(bundle.getString("action.settings")); - builder.removeAllActions(); - builder.addCancelAction(); - builder.addOkAction(); - builder.setCenterPanel(rootPanel); - - return builder; - } - - private ItemListener handleParserChange() { - return e -> { - if(e.getStateChange() == ItemEvent.SELECTED) { - // Automatically suggest file pattern option on parser change - ParserStrategyType newStrategy = ParserStrategyType.fromIndex(parserStrategyComboBox.getSelectedIndex()); - filePatternText.setText(newStrategy.getExampleFilePattern()); - } - }; - } -} \ No newline at end of file diff --git a/src/main/java/de/marhali/easyi18n/editor/KeyAnnotator.java b/src/main/java/de/marhali/easyi18n/editor/KeyAnnotator.java index 919a97a..aa3139a 100644 --- a/src/main/java/de/marhali/easyi18n/editor/KeyAnnotator.java +++ b/src/main/java/de/marhali/easyi18n/editor/KeyAnnotator.java @@ -7,10 +7,10 @@ import com.intellij.openapi.project.Project; import de.marhali.easyi18n.InstanceManager; import de.marhali.easyi18n.model.KeyPath; import de.marhali.easyi18n.model.KeyPathConverter; -import de.marhali.easyi18n.model.SettingsState; import de.marhali.easyi18n.model.TranslationNode; -import de.marhali.easyi18n.service.SettingsService; +import de.marhali.easyi18n.settings.ProjectSettings; +import de.marhali.easyi18n.settings.ProjectSettingsService; import org.jetbrains.annotations.NotNull; /** @@ -27,12 +27,14 @@ public class KeyAnnotator { */ protected void annotate(@NotNull String key, @NotNull Project project, @NotNull AnnotationHolder holder) { // Do not annotate keys if service is disabled - if(!SettingsService.getInstance(project).getState().isCodeAssistance()) { + if(!ProjectSettingsService.get(project).getState().isAssistance()) { return; } - SettingsState state = SettingsService.getInstance(project).getState(); - String pathPrefix = state.getPathPrefix(); + ProjectSettings state = ProjectSettingsService.get(project).getState(); + //String pathPrefix = state.getPathPrefix(); + // TODO: Path prefix removal + String pathPrefix = ""; String previewLocale = state.getPreviewLocale(); KeyPathConverter converter = new KeyPathConverter(project); diff --git a/src/main/java/de/marhali/easyi18n/editor/KeyCompletionProvider.java b/src/main/java/de/marhali/easyi18n/editor/KeyCompletionProvider.java index c866428..ac899d2 100644 --- a/src/main/java/de/marhali/easyi18n/editor/KeyCompletionProvider.java +++ b/src/main/java/de/marhali/easyi18n/editor/KeyCompletionProvider.java @@ -10,8 +10,8 @@ import de.marhali.easyi18n.DataStore; import de.marhali.easyi18n.InstanceManager; import de.marhali.easyi18n.model.KeyPath; import de.marhali.easyi18n.model.Translation; -import de.marhali.easyi18n.service.*; +import de.marhali.easyi18n.settings.ProjectSettingsService; import org.jetbrains.annotations.*; import java.util.*; @@ -29,14 +29,16 @@ public class KeyCompletionProvider extends CompletionProvider 0 && !pathPrefix.endsWith(KeyPath.DELIMITER)) { pathPrefix += KeyPath.DELIMITER; diff --git a/src/main/java/de/marhali/easyi18n/editor/generic/GenericFoldingBuilder.java b/src/main/java/de/marhali/easyi18n/editor/generic/GenericFoldingBuilder.java index cbda752..a54310b 100644 --- a/src/main/java/de/marhali/easyi18n/editor/generic/GenericFoldingBuilder.java +++ b/src/main/java/de/marhali/easyi18n/editor/generic/GenericFoldingBuilder.java @@ -13,7 +13,7 @@ import de.marhali.easyi18n.DataStore; import de.marhali.easyi18n.InstanceManager; import de.marhali.easyi18n.model.KeyPathConverter; import de.marhali.easyi18n.model.Translation; -import de.marhali.easyi18n.service.SettingsService; +import de.marhali.easyi18n.settings.ProjectSettingsService; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -33,7 +33,7 @@ public class GenericFoldingBuilder extends FoldingBuilderEx { Collection literalValues = PsiTreeUtil.findChildrenOfType(root, PsiLiteralValue.class); List descriptors = new ArrayList<>(); - if(!SettingsService.getInstance(root.getProject()).getState().isCodeAssistance()) { + if(!ProjectSettingsService.get(root.getProject()).getState().isAssistance()) { return FoldingDescriptor.EMPTY; } @@ -75,7 +75,7 @@ public class GenericFoldingBuilder extends FoldingBuilderEx { return null; } - String previewLocale = SettingsService.getInstance(literalValue.getProject()).getState().getPreviewLocale(); + String previewLocale = ProjectSettingsService.get(literalValue.getProject()).getState().getPreviewLocale(); return translation.get(previewLocale); } diff --git a/src/main/java/de/marhali/easyi18n/editor/generic/GenericKeyReferenceContributor.java b/src/main/java/de/marhali/easyi18n/editor/generic/GenericKeyReferenceContributor.java index 5127178..9d05418 100644 --- a/src/main/java/de/marhali/easyi18n/editor/generic/GenericKeyReferenceContributor.java +++ b/src/main/java/de/marhali/easyi18n/editor/generic/GenericKeyReferenceContributor.java @@ -7,8 +7,8 @@ import com.intellij.util.ProcessingContext; import de.marhali.easyi18n.InstanceManager; import de.marhali.easyi18n.editor.KeyReference; import de.marhali.easyi18n.model.KeyPathConverter; -import de.marhali.easyi18n.service.SettingsService; +import de.marhali.easyi18n.settings.ProjectSettingsService; import org.jetbrains.annotations.NotNull; /** @@ -35,7 +35,7 @@ public class GenericKeyReferenceContributor extends PsiReferenceContributor { } // Do not reference keys if service is disabled - if(!SettingsService.getInstance(element.getProject()).getState().isCodeAssistance()) { + if(!ProjectSettingsService.get(element.getProject()).getState().isAssistance()) { return PsiReference.EMPTY_ARRAY; } diff --git a/src/main/java/de/marhali/easyi18n/editor/kotlin/KotlinKeyReferenceContributor.java b/src/main/java/de/marhali/easyi18n/editor/kotlin/KotlinKeyReferenceContributor.java index bbb9203..fe37717 100644 --- a/src/main/java/de/marhali/easyi18n/editor/kotlin/KotlinKeyReferenceContributor.java +++ b/src/main/java/de/marhali/easyi18n/editor/kotlin/KotlinKeyReferenceContributor.java @@ -8,8 +8,8 @@ import com.intellij.util.ProcessingContext; import de.marhali.easyi18n.InstanceManager; import de.marhali.easyi18n.editor.KeyReference; import de.marhali.easyi18n.model.KeyPathConverter; -import de.marhali.easyi18n.service.SettingsService; +import de.marhali.easyi18n.settings.ProjectSettingsService; import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.psi.KtLiteralStringTemplateEntry; import org.jetbrains.kotlin.psi.KtStringTemplateExpression; @@ -42,7 +42,7 @@ public class KotlinKeyReferenceContributor extends PsiReferenceContributor { } // Do not reference keys if service is disabled - if(!SettingsService.getInstance(element.getProject()).getState().isCodeAssistance()) { + if(!ProjectSettingsService.get(element.getProject()).getState().isAssistance()) { return PsiReference.EMPTY_ARRAY; } diff --git a/src/main/java/de/marhali/easyi18n/io/IOHandler.java b/src/main/java/de/marhali/easyi18n/io/IOHandler.java index cebe902..dfdf6ab 100644 --- a/src/main/java/de/marhali/easyi18n/io/IOHandler.java +++ b/src/main/java/de/marhali/easyi18n/io/IOHandler.java @@ -9,6 +9,7 @@ import de.marhali.easyi18n.io.parser.ParserStrategy; import de.marhali.easyi18n.io.parser.ParserStrategyType; import de.marhali.easyi18n.model.*; +import de.marhali.easyi18n.settings.ProjectSettings; import org.jetbrains.annotations.NotNull; import java.io.File; @@ -21,23 +22,23 @@ import java.util.List; */ public class IOHandler { - private final @NotNull SettingsState settings; + private final @NotNull ProjectSettings settings; private final @NotNull FolderStrategy folderStrategy; private final @NotNull ParserStrategyType parserStrategyType; private final @NotNull ParserStrategy parserStrategy; - public IOHandler(@NotNull SettingsState settings) throws Exception { + public IOHandler(@NotNull ProjectSettings settings) throws Exception { this.settings = settings; this.folderStrategy = settings.getFolderStrategy().getStrategy() - .getDeclaredConstructor(SettingsState.class).newInstance(settings); + .getDeclaredConstructor(ProjectSettings.class).newInstance(settings); this.parserStrategyType = settings.getParserStrategy(); this.parserStrategy = parserStrategyType.getStrategy() - .getDeclaredConstructor(SettingsState.class).newInstance(settings); + .getDeclaredConstructor(ProjectSettings.class).newInstance(settings); } /** @@ -47,7 +48,7 @@ public class IOHandler { * @throws IOException Could not read translation data */ public @NotNull TranslationData read() throws IOException { - String localesPath = this.settings.getLocalesPath(); + String localesPath = this.settings.getLocalesDirectory(); if(localesPath == null || localesPath.isEmpty()) { throw new EmptyLocalesDirException("Locales path must not be empty"); @@ -59,7 +60,7 @@ public class IOHandler { throw new IllegalArgumentException("Specified locales path is invalid (" + localesPath + ")"); } - TranslationData data = new TranslationData(this.settings.isSortKeys()); + TranslationData data = new TranslationData(this.settings.isSorting()); List translationFiles = this.folderStrategy.analyzeFolderStructure(localesDirectory); for(TranslationFile file : translationFiles) { @@ -80,7 +81,7 @@ public class IOHandler { * @throws IOException Write action failed */ public void write(@NotNull TranslationData data) throws IOException { - String localesPath = this.settings.getLocalesPath(); + String localesPath = this.settings.getLocalesDirectory(); if(localesPath == null || localesPath.isEmpty()) { throw new EmptyLocalesDirException("Locales path must not be empty"); diff --git a/src/main/java/de/marhali/easyi18n/io/folder/FolderStrategy.java b/src/main/java/de/marhali/easyi18n/io/folder/FolderStrategy.java index dbdd316..5e95367 100644 --- a/src/main/java/de/marhali/easyi18n/io/folder/FolderStrategy.java +++ b/src/main/java/de/marhali/easyi18n/io/folder/FolderStrategy.java @@ -4,10 +4,10 @@ import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import de.marhali.easyi18n.io.parser.ParserStrategyType; -import de.marhali.easyi18n.model.SettingsState; import de.marhali.easyi18n.model.TranslationData; import de.marhali.easyi18n.model.TranslationFile; +import de.marhali.easyi18n.settings.ProjectSettings; import org.apache.commons.io.FilenameUtils; import org.jetbrains.annotations.NotNull; @@ -22,9 +22,9 @@ import java.util.Objects; */ public abstract class FolderStrategy { - protected final @NotNull SettingsState settings; + protected final @NotNull ProjectSettings settings; - public FolderStrategy(@NotNull SettingsState settings) { + public FolderStrategy(@NotNull ProjectSettings settings) { this.settings = settings; } diff --git a/src/main/java/de/marhali/easyi18n/io/folder/ModularLocaleFolderStrategy.java b/src/main/java/de/marhali/easyi18n/io/folder/ModularLocaleFolderStrategy.java index e626827..651c9ea 100644 --- a/src/main/java/de/marhali/easyi18n/io/folder/ModularLocaleFolderStrategy.java +++ b/src/main/java/de/marhali/easyi18n/io/folder/ModularLocaleFolderStrategy.java @@ -3,9 +3,9 @@ package de.marhali.easyi18n.io.folder; import com.intellij.openapi.vfs.VirtualFile; import de.marhali.easyi18n.io.parser.ParserStrategyType; -import de.marhali.easyi18n.model.SettingsState; import de.marhali.easyi18n.model.TranslationData; import de.marhali.easyi18n.model.TranslationFile; +import de.marhali.easyi18n.settings.ProjectSettings; import org.jetbrains.annotations.NotNull; @@ -20,7 +20,7 @@ import java.util.List; */ public class ModularLocaleFolderStrategy extends FolderStrategy { - public ModularLocaleFolderStrategy(@NotNull SettingsState settings) { + public ModularLocaleFolderStrategy(@NotNull ProjectSettings settings) { super(settings); } diff --git a/src/main/java/de/marhali/easyi18n/io/folder/ModularNamespaceFolderStrategy.java b/src/main/java/de/marhali/easyi18n/io/folder/ModularNamespaceFolderStrategy.java index 95b6fcd..fe8ffae 100644 --- a/src/main/java/de/marhali/easyi18n/io/folder/ModularNamespaceFolderStrategy.java +++ b/src/main/java/de/marhali/easyi18n/io/folder/ModularNamespaceFolderStrategy.java @@ -3,9 +3,9 @@ package de.marhali.easyi18n.io.folder; import com.intellij.openapi.vfs.VirtualFile; import de.marhali.easyi18n.io.parser.ParserStrategyType; -import de.marhali.easyi18n.model.SettingsState; import de.marhali.easyi18n.model.TranslationData; import de.marhali.easyi18n.model.TranslationFile; +import de.marhali.easyi18n.settings.ProjectSettings; import org.jetbrains.annotations.NotNull; @@ -20,7 +20,7 @@ import java.util.List; */ public class ModularNamespaceFolderStrategy extends FolderStrategy { - public ModularNamespaceFolderStrategy(@NotNull SettingsState settings) { + public ModularNamespaceFolderStrategy(@NotNull ProjectSettings settings) { super(settings); } diff --git a/src/main/java/de/marhali/easyi18n/io/folder/SingleFolderStrategy.java b/src/main/java/de/marhali/easyi18n/io/folder/SingleFolderStrategy.java index 42260a6..4ac6371 100644 --- a/src/main/java/de/marhali/easyi18n/io/folder/SingleFolderStrategy.java +++ b/src/main/java/de/marhali/easyi18n/io/folder/SingleFolderStrategy.java @@ -3,9 +3,9 @@ package de.marhali.easyi18n.io.folder; import com.intellij.openapi.vfs.VirtualFile; import de.marhali.easyi18n.io.parser.ParserStrategyType; -import de.marhali.easyi18n.model.SettingsState; import de.marhali.easyi18n.model.TranslationData; import de.marhali.easyi18n.model.TranslationFile; +import de.marhali.easyi18n.settings.ProjectSettings; import org.jetbrains.annotations.NotNull; @@ -22,7 +22,7 @@ import java.util.List; */ public class SingleFolderStrategy extends FolderStrategy { - public SingleFolderStrategy(@NotNull SettingsState settings) { + public SingleFolderStrategy(@NotNull ProjectSettings settings) { super(settings); } diff --git a/src/main/java/de/marhali/easyi18n/io/parser/ParserStrategy.java b/src/main/java/de/marhali/easyi18n/io/parser/ParserStrategy.java index 1678569..07bc38c 100644 --- a/src/main/java/de/marhali/easyi18n/io/parser/ParserStrategy.java +++ b/src/main/java/de/marhali/easyi18n/io/parser/ParserStrategy.java @@ -2,6 +2,7 @@ package de.marhali.easyi18n.io.parser; import de.marhali.easyi18n.model.*; +import de.marhali.easyi18n.settings.ProjectSettings; import org.jetbrains.annotations.NotNull; import java.util.Objects; @@ -12,9 +13,9 @@ import java.util.Objects; */ public abstract class ParserStrategy { - protected final @NotNull SettingsState settings; + protected final @NotNull ProjectSettings settings; - public ParserStrategy(@NotNull SettingsState settings) { + public ParserStrategy(@NotNull ProjectSettings settings) { this.settings = settings; } @@ -48,7 +49,7 @@ public abstract class ParserStrategy { TranslationNode moduleNode = data.getNode(KeyPath.of(moduleName)); if(moduleNode == null) { - moduleNode = new TranslationNode(this.settings.isSortKeys()); + moduleNode = new TranslationNode(this.settings.isSorting()); data.getRootNode().setChildren(moduleName, moduleNode); } diff --git a/src/main/java/de/marhali/easyi18n/io/parser/json/JsonParserStrategy.java b/src/main/java/de/marhali/easyi18n/io/parser/json/JsonParserStrategy.java index a765547..f47ac76 100644 --- a/src/main/java/de/marhali/easyi18n/io/parser/json/JsonParserStrategy.java +++ b/src/main/java/de/marhali/easyi18n/io/parser/json/JsonParserStrategy.java @@ -3,14 +3,15 @@ package de.marhali.easyi18n.io.parser.json; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonObject; + import com.intellij.openapi.vfs.VirtualFile; import de.marhali.easyi18n.io.parser.ParserStrategy; import de.marhali.easyi18n.model.*; +import de.marhali.easyi18n.settings.ProjectSettings; import org.jetbrains.annotations.NotNull; -import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; import java.util.Objects; @@ -23,7 +24,7 @@ public class JsonParserStrategy extends ParserStrategy { private static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create(); - public JsonParserStrategy(@NotNull SettingsState settings) { + public JsonParserStrategy(@NotNull ProjectSettings settings) { super(settings); } diff --git a/src/main/java/de/marhali/easyi18n/io/parser/json5/Json5ParserStrategy.java b/src/main/java/de/marhali/easyi18n/io/parser/json5/Json5ParserStrategy.java index cb9c64b..97965b9 100644 --- a/src/main/java/de/marhali/easyi18n/io/parser/json5/Json5ParserStrategy.java +++ b/src/main/java/de/marhali/easyi18n/io/parser/json5/Json5ParserStrategy.java @@ -3,10 +3,10 @@ package de.marhali.easyi18n.io.parser.json5; import com.intellij.openapi.vfs.VirtualFile; import de.marhali.easyi18n.io.parser.ParserStrategy; -import de.marhali.easyi18n.model.SettingsState; import de.marhali.easyi18n.model.TranslationData; import de.marhali.easyi18n.model.TranslationFile; import de.marhali.easyi18n.model.TranslationNode; +import de.marhali.easyi18n.settings.ProjectSettings; import de.marhali.json5.Json5; import de.marhali.json5.Json5Element; import de.marhali.json5.Json5Object; @@ -26,7 +26,7 @@ public class Json5ParserStrategy extends ParserStrategy { private static final Json5 JSON5 = Json5.builder(builder -> builder.allowInvalidSurrogate().trailingComma().indentFactor(4).build()); - public Json5ParserStrategy(@NotNull SettingsState settings) { + public Json5ParserStrategy(@NotNull ProjectSettings settings) { super(settings); } diff --git a/src/main/java/de/marhali/easyi18n/io/parser/properties/PropertiesParserStrategy.java b/src/main/java/de/marhali/easyi18n/io/parser/properties/PropertiesParserStrategy.java index 35a45db..8f29534 100644 --- a/src/main/java/de/marhali/easyi18n/io/parser/properties/PropertiesParserStrategy.java +++ b/src/main/java/de/marhali/easyi18n/io/parser/properties/PropertiesParserStrategy.java @@ -3,10 +3,10 @@ package de.marhali.easyi18n.io.parser.properties; import com.intellij.openapi.vfs.VirtualFile; import de.marhali.easyi18n.io.parser.ParserStrategy; -import de.marhali.easyi18n.model.SettingsState; import de.marhali.easyi18n.model.TranslationData; import de.marhali.easyi18n.model.TranslationFile; import de.marhali.easyi18n.model.TranslationNode; +import de.marhali.easyi18n.settings.ProjectSettings; import org.jetbrains.annotations.NotNull; @@ -20,7 +20,7 @@ import java.io.StringWriter; */ public class PropertiesParserStrategy extends ParserStrategy { - public PropertiesParserStrategy(@NotNull SettingsState settings) { + public PropertiesParserStrategy(@NotNull ProjectSettings settings) { super(settings); } @@ -33,7 +33,7 @@ public class PropertiesParserStrategy extends ParserStrategy { TranslationData targetData = new TranslationData(data.getLocales(), targetNode); try(Reader reader = new InputStreamReader(vf.getInputStream(), vf.getCharset())) { - SortableProperties input = new SortableProperties(this.settings.isSortKeys()); + SortableProperties input = new SortableProperties(this.settings.isSorting()); input.load(reader); PropertiesMapper.read(file.getLocale(), input, targetData); } @@ -44,7 +44,7 @@ public class PropertiesParserStrategy extends ParserStrategy { TranslationNode targetNode = super.getTargetNode(data, file); TranslationData targetData = new TranslationData(data.getLocales(), targetNode); - SortableProperties output = new SortableProperties(this.settings.isSortKeys()); + SortableProperties output = new SortableProperties(this.settings.isSorting()); PropertiesMapper.write(file.getLocale(), output, targetData); try(StringWriter writer = new StringWriter()) { diff --git a/src/main/java/de/marhali/easyi18n/io/parser/yaml/YamlParserStrategy.java b/src/main/java/de/marhali/easyi18n/io/parser/yaml/YamlParserStrategy.java index dddf5ec..5ea78ad 100644 --- a/src/main/java/de/marhali/easyi18n/io/parser/yaml/YamlParserStrategy.java +++ b/src/main/java/de/marhali/easyi18n/io/parser/yaml/YamlParserStrategy.java @@ -3,10 +3,10 @@ package de.marhali.easyi18n.io.parser.yaml; import com.intellij.openapi.vfs.VirtualFile; import de.marhali.easyi18n.io.parser.ParserStrategy; -import de.marhali.easyi18n.model.SettingsState; import de.marhali.easyi18n.model.TranslationData; import de.marhali.easyi18n.model.TranslationFile; import de.marhali.easyi18n.model.TranslationNode; +import de.marhali.easyi18n.settings.ProjectSettings; import org.jetbrains.annotations.NotNull; @@ -22,7 +22,7 @@ import java.io.Reader; */ public class YamlParserStrategy extends ParserStrategy { - public YamlParserStrategy(@NotNull SettingsState settings) { + public YamlParserStrategy(@NotNull ProjectSettings settings) { super(settings); } diff --git a/src/main/java/de/marhali/easyi18n/model/KeyPathConverter.java b/src/main/java/de/marhali/easyi18n/model/KeyPathConverter.java index c5b0ecd..cfb9d26 100644 --- a/src/main/java/de/marhali/easyi18n/model/KeyPathConverter.java +++ b/src/main/java/de/marhali/easyi18n/model/KeyPathConverter.java @@ -2,7 +2,7 @@ package de.marhali.easyi18n.model; import com.intellij.openapi.project.Project; -import de.marhali.easyi18n.service.SettingsService; +import de.marhali.easyi18n.settings.ProjectSettingsService; import org.jetbrains.annotations.NotNull; @@ -22,7 +22,7 @@ public class KeyPathConverter { } public KeyPathConverter(@NotNull Project project) { - this(SettingsService.getInstance(project).getState().isNestedKeys()); + this(ProjectSettingsService.get(project).getState().isNestedKeys()); } public @NotNull String concat(@NotNull KeyPath path) { diff --git a/src/main/java/de/marhali/easyi18n/model/SettingsState.java b/src/main/java/de/marhali/easyi18n/model/SettingsState.java deleted file mode 100644 index 4e5c810..0000000 --- a/src/main/java/de/marhali/easyi18n/model/SettingsState.java +++ /dev/null @@ -1,106 +0,0 @@ -package de.marhali.easyi18n.model; - -import de.marhali.easyi18n.io.parser.ParserStrategyType; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * Represents the persistent settings which can be configured. - * @author marhali - */ -@Deprecated -public class SettingsState { - - public static final String DEFAULT_PREVIEW_LOCALE = "en"; - public static final FolderStrategyType DEFAULT_FOLDER_STRATEGY = FolderStrategyType.SINGLE; - public static final ParserStrategyType DEFAULT_PARSER_STRATEGY = ParserStrategyType.JSON; - public static final String DEFAULT_FILE_PATTERN = "*.*"; - public static final String DEFAULT_PATH_PREFIX = ""; - public static final boolean DEFAULT_SORT_KEYS = true; - public static final boolean DEFAULT_NESTED_KEYS = true; - public static final boolean DEFAULT_CODE_ASSISTANCE = true; - - private String localesPath; - private FolderStrategyType folderStrategy; - private ParserStrategyType parserStrategy; - private String filePattern; - private String previewLocale; - private String pathPrefix; - private Boolean sortKeys; - private Boolean nestedKeys; - private Boolean codeAssistance; - - public SettingsState() {} - - public @Nullable String getLocalesPath() { - return localesPath; - } - - public void setLocalesPath(String localesPath) { - this.localesPath = localesPath; - } - - public @NotNull FolderStrategyType getFolderStrategy() { - return folderStrategy != null ? folderStrategy : DEFAULT_FOLDER_STRATEGY; - } - - public void setFolderStrategy(FolderStrategyType folderStrategy) { - this.folderStrategy = folderStrategy; - } - - public @NotNull ParserStrategyType getParserStrategy() { - return parserStrategy != null ? parserStrategy : DEFAULT_PARSER_STRATEGY; - } - - public void setParserStrategy(ParserStrategyType parserStrategy) { - this.parserStrategy = parserStrategy; - } - - public @NotNull String getFilePattern() { - return filePattern != null ? filePattern : DEFAULT_FILE_PATTERN; - } - - public void setFilePattern(String filePattern) { - this.filePattern = filePattern; - } - - public @NotNull String getPreviewLocale() { - return previewLocale != null ? previewLocale : DEFAULT_PREVIEW_LOCALE; - } - - public void setPreviewLocale(String previewLocale) { - this.previewLocale = previewLocale; - } - - public @NotNull String getPathPrefix() { - return pathPrefix != null ? pathPrefix : DEFAULT_PATH_PREFIX; - } - - public void setPathPrefix(String pathPrefix) { - this.pathPrefix = pathPrefix; - } - - public boolean isSortKeys() { - return sortKeys == null ? DEFAULT_SORT_KEYS : sortKeys; - } - - public void setSortKeys(boolean sortKeys) { - this.sortKeys = sortKeys; - } - - public boolean isNestedKeys() { - return nestedKeys == null ? DEFAULT_NESTED_KEYS : nestedKeys; - } - - public void setNestedKeys(boolean nestedKeys) { - this.nestedKeys = nestedKeys; - } - - public boolean isCodeAssistance() { - return codeAssistance == null ? DEFAULT_CODE_ASSISTANCE : codeAssistance; - } - - public void setCodeAssistance(boolean codeAssistance) { - this.codeAssistance = codeAssistance; - } -} \ No newline at end of file diff --git a/src/main/java/de/marhali/easyi18n/service/SettingsService.java b/src/main/java/de/marhali/easyi18n/service/SettingsService.java deleted file mode 100644 index 9460fa8..0000000 --- a/src/main/java/de/marhali/easyi18n/service/SettingsService.java +++ /dev/null @@ -1,42 +0,0 @@ -package de.marhali.easyi18n.service; - -import com.intellij.openapi.components.PersistentStateComponent; -import com.intellij.openapi.components.State; -import com.intellij.openapi.project.Project; - -import de.marhali.easyi18n.model.SettingsState; - -import org.jetbrains.annotations.NotNull; - -/** - * Persistent settings storage at project level. - * @author marhali - */ -@State(name = "EasyI18nSettings") -@Deprecated -public class SettingsService implements PersistentStateComponent { - - public static SettingsService getInstance(Project project) { - return project.getService(SettingsService.class); - } - - private SettingsState state; - - public SettingsService() { - this.state = new SettingsState(); - } - - @Override - public @NotNull SettingsState getState() { - return state; - } - - public void setState(SettingsState state) { - this.state = state; - } - - @Override - public void loadState(@NotNull SettingsState state) { - this.state = state; - } -} diff --git a/src/main/java/de/marhali/easyi18n/tabs/TreeView.java b/src/main/java/de/marhali/easyi18n/tabs/TreeView.java index dff10ea..670e907 100644 --- a/src/main/java/de/marhali/easyi18n/tabs/TreeView.java +++ b/src/main/java/de/marhali/easyi18n/tabs/TreeView.java @@ -18,7 +18,7 @@ import de.marhali.easyi18n.dialog.EditDialog; import de.marhali.easyi18n.listener.DeleteKeyListener; import de.marhali.easyi18n.listener.PopupClickListener; import de.marhali.easyi18n.renderer.TreeRenderer; -import de.marhali.easyi18n.service.SettingsService; +import de.marhali.easyi18n.settings.ProjectSettingsService; import de.marhali.easyi18n.tabs.mapper.TreeModelMapper; import de.marhali.easyi18n.util.TreeUtil; @@ -80,7 +80,7 @@ public class TreeView implements BusListener { @Override public void onUpdateData(@NotNull TranslationData data) { - tree.setModel(this.currentMapper = new TreeModelMapper(data, SettingsService.getInstance(project).getState())); + tree.setModel(this.currentMapper = new TreeModelMapper(data, ProjectSettingsService.get(project).getState())); } @Override diff --git a/src/main/java/de/marhali/easyi18n/tabs/mapper/TreeModelMapper.java b/src/main/java/de/marhali/easyi18n/tabs/mapper/TreeModelMapper.java index e7468cb..e337cc3 100644 --- a/src/main/java/de/marhali/easyi18n/tabs/mapper/TreeModelMapper.java +++ b/src/main/java/de/marhali/easyi18n/tabs/mapper/TreeModelMapper.java @@ -6,6 +6,7 @@ import com.intellij.ui.JBColor; import de.marhali.easyi18n.model.*; import de.marhali.easyi18n.model.bus.FilterMissingTranslationsListener; import de.marhali.easyi18n.model.bus.SearchQueryListener; +import de.marhali.easyi18n.settings.ProjectSettings; import de.marhali.easyi18n.util.UiUtil; import org.jetbrains.annotations.NotNull; @@ -24,9 +25,9 @@ public class TreeModelMapper extends DefaultTreeModel implements SearchQueryList private final TranslationData data; private final KeyPathConverter converter; - private final SettingsState state; + private final ProjectSettings state; - public TreeModelMapper(TranslationData data, SettingsState state) { + public TreeModelMapper(TranslationData data, ProjectSettings state) { super(null); this.data = data; @@ -41,7 +42,7 @@ public class TreeModelMapper extends DefaultTreeModel implements SearchQueryList @Override public void onSearchQuery(@Nullable String query) { DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode(); - TranslationData shadow = new TranslationData(this.state.isSortKeys()); + TranslationData shadow = new TranslationData(this.state.isSorting()); if(query == null) { // Reset this.generateNodes(rootNode, this.data.getRootNode()); @@ -75,7 +76,7 @@ public class TreeModelMapper extends DefaultTreeModel implements SearchQueryList @Override public void onFilterMissingTranslations(boolean filter) { DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode(); - TranslationData shadow = new TranslationData(this.state.isSortKeys()); + TranslationData shadow = new TranslationData(this.state.isSorting()); if(!filter) { // Reset this.generateNodes(rootNode, this.data.getRootNode()); diff --git a/src/main/java/de/marhali/easyi18n/util/NotificationHelper.java b/src/main/java/de/marhali/easyi18n/util/NotificationHelper.java index 70437b8..ea1393c 100644 --- a/src/main/java/de/marhali/easyi18n/util/NotificationHelper.java +++ b/src/main/java/de/marhali/easyi18n/util/NotificationHelper.java @@ -5,7 +5,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import de.marhali.easyi18n.action.SettingsAction; import de.marhali.easyi18n.io.IOHandler; -import de.marhali.easyi18n.model.SettingsState; +import de.marhali.easyi18n.settings.ProjectSettings; import org.jetbrains.annotations.NotNull; import java.text.MessageFormat; @@ -17,11 +17,11 @@ import java.util.ResourceBundle; */ public class NotificationHelper { - public static void createIOError(@NotNull SettingsState state, Exception ex) { + public static void createIOError(@NotNull ProjectSettings state, Exception ex) { ResourceBundle bundle = ResourceBundle.getBundle("messages"); String message = MessageFormat.format(bundle.getString("error.io"), - state.getFolderStrategy(), state.getParserStrategy(), state.getFilePattern(), state.getLocalesPath()); + state.getFolderStrategy(), state.getParserStrategy(), state.getFilePattern(), state.getLocalesDirectory()); Logger.getInstance(IOHandler.class).error(message, ex); } From 289f168e8828959dd58c5578b322eb98c72c3a93 Mon Sep 17 00:00:00 2001 From: marhali Date: Sat, 9 Apr 2022 13:00:43 +0200 Subject: [PATCH 09/51] introduce configuration presets --- .../settings/presets/DefaultPreset.java | 79 +++++++++++++++++++ .../easyi18n/settings/presets/Preset.java | 33 ++++++++ .../settings/presets/ReactI18NextPreset.java | 79 +++++++++++++++++++ .../settings/presets/VueI18nPreset.java | 78 ++++++++++++++++++ 4 files changed, 269 insertions(+) create mode 100644 src/main/java/de/marhali/easyi18n/settings/presets/DefaultPreset.java create mode 100644 src/main/java/de/marhali/easyi18n/settings/presets/Preset.java create mode 100644 src/main/java/de/marhali/easyi18n/settings/presets/ReactI18NextPreset.java create mode 100644 src/main/java/de/marhali/easyi18n/settings/presets/VueI18nPreset.java diff --git a/src/main/java/de/marhali/easyi18n/settings/presets/DefaultPreset.java b/src/main/java/de/marhali/easyi18n/settings/presets/DefaultPreset.java new file mode 100644 index 0000000..fb515fe --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/settings/presets/DefaultPreset.java @@ -0,0 +1,79 @@ +package de.marhali.easyi18n.settings.presets; + +import de.marhali.easyi18n.io.parser.ParserStrategyType; +import de.marhali.easyi18n.model.FolderStrategyType; +import de.marhali.easyi18n.settings.ProjectSettings; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Default preset. Used if none has been defined. + * @author marhali + */ +public class DefaultPreset implements ProjectSettings { + @Override + public String getLocalesDirectory() { + return null; + } + + @Override + public @NotNull FolderStrategyType getFolderStrategy() { + return FolderStrategyType.SINGLE; + } + + @Override + public @NotNull ParserStrategyType getParserStrategy() { + return ParserStrategyType.JSON; + } + + @Override + public @NotNull String getFilePattern() { + return "*.*"; + } + + @Override + public boolean isSorting() { + return true; + } + + @Override + public String getNamespaceDelimiter() { + return ":"; + } + + @Override + public @NotNull String getSectionDelimiter() { + return "."; + } + + @Override + public String getContextDelimiter() { + return "_"; + } + + @Override + public String getPluralDelimiter() { + return "_"; + } + + @Override + public @Nullable String getDefaultNamespace() { + return null; + } + + @Override + public @NotNull String getPreviewLocale() { + return "en"; + } + + @Override + public boolean isNestedKeys() { + return true; + } + + @Override + public boolean isAssistance() { + return true; + } +} diff --git a/src/main/java/de/marhali/easyi18n/settings/presets/Preset.java b/src/main/java/de/marhali/easyi18n/settings/presets/Preset.java new file mode 100644 index 0000000..7a7155a --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/settings/presets/Preset.java @@ -0,0 +1,33 @@ +package de.marhali.easyi18n.settings.presets; + +import de.marhali.easyi18n.settings.ProjectSettings; + +/** + * Enumeration of all available configuration presets. + * Every preset needs to be registered here to be properly recognized. + * @author marhali + */ +public enum Preset { + DEFAULT(DefaultPreset.class), + VUE_I18N(VueI18nPreset.class), + REACT_I18NEXT(ReactI18NextPreset.class); + + private final Class clazz; + + Preset(Class clazz) { + this.clazz = clazz; + } + + public ProjectSettings config() { + try { + return this.clazz.getDeclaredConstructor().newInstance(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public String toString() { + return super.name().toLowerCase(); + } +} diff --git a/src/main/java/de/marhali/easyi18n/settings/presets/ReactI18NextPreset.java b/src/main/java/de/marhali/easyi18n/settings/presets/ReactI18NextPreset.java new file mode 100644 index 0000000..2a76af3 --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/settings/presets/ReactI18NextPreset.java @@ -0,0 +1,79 @@ +package de.marhali.easyi18n.settings.presets; + +import de.marhali.easyi18n.io.parser.ParserStrategyType; +import de.marhali.easyi18n.model.FolderStrategyType; +import de.marhali.easyi18n.settings.ProjectSettings; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Preset for React - i18n-next + * @author marhali + */ +public class ReactI18NextPreset implements ProjectSettings { + @Override + public @Nullable String getLocalesDirectory() { + return null; + } + + @Override + public @NotNull FolderStrategyType getFolderStrategy() { + return FolderStrategyType.MODULARIZED_NAMESPACE; + } + + @Override + public @NotNull ParserStrategyType getParserStrategy() { + return ParserStrategyType.JSON; + } + + @Override + public @NotNull String getFilePattern() { + return "*.json"; + } + + @Override + public boolean isSorting() { + return true; + } + + @Override + public @Nullable String getNamespaceDelimiter() { + return ":"; + } + + @Override + public @NotNull String getSectionDelimiter() { + return "."; + } + + @Override + public @Nullable String getContextDelimiter() { + return "_"; + } + + @Override + public @Nullable String getPluralDelimiter() { + return "_"; + } + + @Override + public @Nullable String getDefaultNamespace() { + return "common"; + } + + @Override + public @NotNull String getPreviewLocale() { + return "en"; + } + + @Override + public boolean isNestedKeys() { + return false; + } + + @Override + public boolean isAssistance() { + return true; + } +} diff --git a/src/main/java/de/marhali/easyi18n/settings/presets/VueI18nPreset.java b/src/main/java/de/marhali/easyi18n/settings/presets/VueI18nPreset.java new file mode 100644 index 0000000..83c3276 --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/settings/presets/VueI18nPreset.java @@ -0,0 +1,78 @@ +package de.marhali.easyi18n.settings.presets; + +import de.marhali.easyi18n.io.parser.ParserStrategyType; +import de.marhali.easyi18n.model.FolderStrategyType; +import de.marhali.easyi18n.settings.ProjectSettings; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Preset for Vue.js - vue-i18n + * @author marhali + */ +public class VueI18nPreset implements ProjectSettings { + @Override + public @Nullable String getLocalesDirectory() { + return null; + } + + @Override + public @NotNull FolderStrategyType getFolderStrategy() { + return FolderStrategyType.SINGLE; + } + + @Override + public @NotNull ParserStrategyType getParserStrategy() { + return ParserStrategyType.JSON; + } + + @Override + public @NotNull String getFilePattern() { + return "*.json"; + } + + @Override + public boolean isSorting() { + return true; + } + + @Override + public @Nullable String getNamespaceDelimiter() { + return null; + } + + @Override + public @NotNull String getSectionDelimiter() { + return "."; + } + + @Override + public @Nullable String getContextDelimiter() { + return null; + } + + @Override + public @Nullable String getPluralDelimiter() { + return null; + } + + @Override + public @Nullable String getDefaultNamespace() { + return null; + } + + @Override + public @NotNull String getPreviewLocale() { + return "en"; + } + + @Override + public boolean isNestedKeys() { + return true; + } + + @Override + public boolean isAssistance() { + return true; + } +} From 1f03d51b7323683dba58c5a1488d9e671001bf47 Mon Sep 17 00:00:00 2001 From: marhali Date: Sat, 9 Apr 2022 14:15:14 +0200 Subject: [PATCH 10/51] add tool window icon --- CHANGELOG.md | 5 ++++- src/main/resources/META-INF/plugin.xml | 3 ++- src/main/resources/META-INF/toolWindowIcon.svg | 9 --------- src/main/resources/icons/translate13.svg | 4 ++++ src/main/resources/icons/translate13_dark.svg | 4 ++++ 5 files changed, 14 insertions(+), 11 deletions(-) delete mode 100644 src/main/resources/META-INF/toolWindowIcon.svg create mode 100644 src/main/resources/icons/translate13.svg create mode 100644 src/main/resources/icons/translate13_dark.svg diff --git a/CHANGELOG.md b/CHANGELOG.md index f133621..d159c2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,11 @@ # easy-i18n Changelog ## [Unreleased] +### Added +- Individual icon to tool-window + ### Fixed -- AlreadyDisposedException on FileChangeListener after project has been disposed +- AlreadyDisposedException on FileChangeListener after project dispose ## [3.2.0] ### Added diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index a0c3ee7..8df51ed 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -25,7 +25,8 @@ + factoryClass="de.marhali.easyi18n.service.TranslatorToolWindowFactory" + icon="/icons/translate13.svg"/> diff --git a/src/main/resources/META-INF/toolWindowIcon.svg b/src/main/resources/META-INF/toolWindowIcon.svg deleted file mode 100644 index a768feb..0000000 --- a/src/main/resources/META-INF/toolWindowIcon.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - Layer 1 - - - - - \ No newline at end of file diff --git a/src/main/resources/icons/translate13.svg b/src/main/resources/icons/translate13.svg new file mode 100644 index 0000000..fbe7f69 --- /dev/null +++ b/src/main/resources/icons/translate13.svg @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/src/main/resources/icons/translate13_dark.svg b/src/main/resources/icons/translate13_dark.svg new file mode 100644 index 0000000..c43d969 --- /dev/null +++ b/src/main/resources/icons/translate13_dark.svg @@ -0,0 +1,4 @@ + + + \ No newline at end of file From f3a1da9d4b1e3e5194254a3a2b7764b7524b7389 Mon Sep 17 00:00:00 2001 From: marhali Date: Sat, 9 Apr 2022 14:16:45 +0200 Subject: [PATCH 11/51] update roadmap --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ba600c3..bfd9fb2 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,7 @@ _For more examples, please refer to the [Examples Directory](https://github.com/ - [ ] Mark duplicate translation values - [ ] Configurable namespace and section separators - [ ] Define default namespace to use if none was provided +- [ ] Enhance editor code assistance See the [open issues](https://github.com/marhali/easy-i18n/issues) for a full list of proposed features (and known issues). From 7e18a6b87805b453e20e5da088fd0c3d2fcabdc2 Mon Sep 17 00:00:00 2001 From: marhali Date: Sat, 9 Apr 2022 17:37:45 +0200 Subject: [PATCH 12/51] update settings hint --- src/main/resources/messages.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties index 2c4620c..ddad8b2 100644 --- a/src/main/resources/messages.properties +++ b/src/main/resources/messages.properties @@ -13,7 +13,7 @@ action.delete=Delete translation.key=Key translation.locales=Locales # Settings -settings.hint.text=Project-specific configuration for using your translation files. For an easy start, you can use one of the existing presets. +settings.hint.text=Project-specific configuration. For an easy start, you can use one of the existing presets. settings.hint.action=Fore more information, see the documentation settings.preset.title=Preset settings.preset.tooltip=Choose a configuration template that best fits your project. After that you can make further changes. From 02ff35245b8a2a94adcd31759b605dedccdbc431 Mon Sep 17 00:00:00 2001 From: marhali Date: Sun, 10 Apr 2022 13:00:07 +0200 Subject: [PATCH 13/51] optimize delimiter layout --- src/main/resources/messages.properties | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties index ddad8b2..4b485b6 100644 --- a/src/main/resources/messages.properties +++ b/src/main/resources/messages.properties @@ -37,10 +37,10 @@ settings.editor.key.namespace.title=[namespace] settings.editor.key.namespace.tooltip=Sets the separator used between namespace and key path. settings.editor.key.section.title=[section] settings.editor.key.section.tooltip=Sets the separator used between section nodes of the key path. -settings.editor.key.node.title=[node] -settings.editor.key.context.title=[context] +settings.editor.key.node.title=[node +settings.editor.key.context.title=context settings.editor.key.context.tooltip=Sets the separator used to define context-specific variants of a translation. -settings.editor.key.plural.title=[pluralization] +settings.editor.key.plural.title=pluralization] settings.editor.key.plural.tooltip=Sets the separator used to define different pluralization's of a translation. settings.editor.default-namespace.title=Default namespace settings.editor.default-namespace.tooltip=Specifies the namespace to use by default if none is specified in a translation key. The field can be left blank to ignore this feature. From ddebb04871ce1f364e6e506b23526f1c284bb8e9 Mon Sep 17 00:00:00 2001 From: marhali Date: Sun, 10 Apr 2022 13:00:48 +0200 Subject: [PATCH 14/51] remove legacy structure --- .../model/translation/variant/ContextMap.java | 11 ----------- .../model/translation/variant/LocaleMap.java | 17 ----------------- .../model/translation/variant/Plural.java | 11 ----------- .../model/translation/variant/PluralMap.java | 9 --------- 4 files changed, 48 deletions(-) delete mode 100644 src/main/java/de/marhali/easyi18n/model/translation/variant/ContextMap.java delete mode 100644 src/main/java/de/marhali/easyi18n/model/translation/variant/LocaleMap.java delete mode 100644 src/main/java/de/marhali/easyi18n/model/translation/variant/Plural.java delete mode 100644 src/main/java/de/marhali/easyi18n/model/translation/variant/PluralMap.java diff --git a/src/main/java/de/marhali/easyi18n/model/translation/variant/ContextMap.java b/src/main/java/de/marhali/easyi18n/model/translation/variant/ContextMap.java deleted file mode 100644 index 0f4e10e..0000000 --- a/src/main/java/de/marhali/easyi18n/model/translation/variant/ContextMap.java +++ /dev/null @@ -1,11 +0,0 @@ -package de.marhali.easyi18n.model.translation.variant; - -import java.util.HashMap; - -/** - * Maps context with pluralization. - * @author marhali - */ -public class ContextMap extends HashMap { - public static final String DEFAULT = "default"; -} diff --git a/src/main/java/de/marhali/easyi18n/model/translation/variant/LocaleMap.java b/src/main/java/de/marhali/easyi18n/model/translation/variant/LocaleMap.java deleted file mode 100644 index 76a7cf4..0000000 --- a/src/main/java/de/marhali/easyi18n/model/translation/variant/LocaleMap.java +++ /dev/null @@ -1,17 +0,0 @@ -package de.marhali.easyi18n.model.translation.variant; - -import java.util.HashMap; -import java.util.Map; - -/** - * Maps locale type with specified value - * @author marhali - */ -public class LocaleMap extends HashMap { - - public LocaleMap() {} - - public LocaleMap(Map m) { - super(m); - } -} \ No newline at end of file diff --git a/src/main/java/de/marhali/easyi18n/model/translation/variant/Plural.java b/src/main/java/de/marhali/easyi18n/model/translation/variant/Plural.java deleted file mode 100644 index 7de4543..0000000 --- a/src/main/java/de/marhali/easyi18n/model/translation/variant/Plural.java +++ /dev/null @@ -1,11 +0,0 @@ -package de.marhali.easyi18n.model.translation.variant; - -/** - * Represents all possible pluralization forms a translation can support. - * @author marhali - */ -public enum Plural { - ZERO, ONE, TWO, FEW, MANY, OTHER; - - public static final Plural DEFAULT = Plural.ONE; -} diff --git a/src/main/java/de/marhali/easyi18n/model/translation/variant/PluralMap.java b/src/main/java/de/marhali/easyi18n/model/translation/variant/PluralMap.java deleted file mode 100644 index a79047f..0000000 --- a/src/main/java/de/marhali/easyi18n/model/translation/variant/PluralMap.java +++ /dev/null @@ -1,9 +0,0 @@ -package de.marhali.easyi18n.model.translation.variant; - -import java.util.HashMap; - -/** - * Maps pluralization with locale values. - * @author marhali - */ -public class PluralMap extends HashMap {} From f63492ea7a3d3183daa9c5d3b5002299d50e3393 Mon Sep 17 00:00:00 2001 From: marhali Date: Sun, 10 Apr 2022 13:11:51 +0200 Subject: [PATCH 15/51] optimize delimiter naming --- .../easyi18n/settings/ProjectSettingsComponent.java | 13 ++++++++++--- src/main/resources/messages.properties | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/main/java/de/marhali/easyi18n/settings/ProjectSettingsComponent.java b/src/main/java/de/marhali/easyi18n/settings/ProjectSettingsComponent.java index 95f38e6..cb84f19 100644 --- a/src/main/java/de/marhali/easyi18n/settings/ProjectSettingsComponent.java +++ b/src/main/java/de/marhali/easyi18n/settings/ProjectSettingsComponent.java @@ -156,15 +156,22 @@ public class ProjectSettingsComponent extends ProjectSettingsComponentState { panel.add(namespaceDelimiter = createDelimiterField(bundle.getString("settings.editor.key.namespace.tooltip"))); panel.add(new JBLabel(bundle.getString("settings.editor.key.section.title"))); panel.add(sectionDelimiter = createDelimiterField(bundle.getString("settings.editor.key.section.tooltip"))); - panel.add(new JBLabel(bundle.getString("settings.editor.key.node.title"))); + panel.add(createBoldLabel(bundle.getString("settings.editor.key.leaf.title"))); panel.add(contextDelimiter = createDelimiterField(bundle.getString("settings.editor.key.context.tooltip"))); - panel.add(new JBLabel(bundle.getString("settings.editor.key.context.title"))); + panel.add(createBoldLabel(bundle.getString("settings.editor.key.context.title"))); panel.add(pluralDelimiter = createDelimiterField(bundle.getString("settings.editor.key.plural.tooltip"))); - panel.add(new JBLabel(bundle.getString("settings.editor.key.plural.title"))); + panel.add(createBoldLabel(bundle.getString("settings.editor.key.plural.title"))); return panel; } + private JLabel createBoldLabel(String title) { + JBLabel label = new JBLabel(title); + Font font = label.getFont(); + label.setFont(font.deriveFont(font.getStyle() | Font.BOLD)); + return label; + } + private JTextField createDelimiterField(String tooltip) { JBTextField field = new JBTextField(); field.setHorizontalAlignment(JTextField.CENTER); diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties index 4b485b6..5d3cc88 100644 --- a/src/main/resources/messages.properties +++ b/src/main/resources/messages.properties @@ -37,7 +37,7 @@ settings.editor.key.namespace.title=[namespace] settings.editor.key.namespace.tooltip=Sets the separator used between namespace and key path. settings.editor.key.section.title=[section] settings.editor.key.section.tooltip=Sets the separator used between section nodes of the key path. -settings.editor.key.node.title=[node +settings.editor.key.leaf.title=[leaf settings.editor.key.context.title=context settings.editor.key.context.tooltip=Sets the separator used to define context-specific variants of a translation. settings.editor.key.plural.title=pluralization] From beb4a89df538b30778b37229fd68a7942f8b0d32 Mon Sep 17 00:00:00 2001 From: marhali Date: Sun, 10 Apr 2022 13:43:21 +0200 Subject: [PATCH 16/51] introduce namespace mode --- .../easyi18n/model/FolderStrategyType.java | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/main/java/de/marhali/easyi18n/model/FolderStrategyType.java b/src/main/java/de/marhali/easyi18n/model/FolderStrategyType.java index 14e81f3..78d4daf 100644 --- a/src/main/java/de/marhali/easyi18n/model/FolderStrategyType.java +++ b/src/main/java/de/marhali/easyi18n/model/FolderStrategyType.java @@ -10,14 +10,20 @@ import de.marhali.easyi18n.io.folder.SingleFolderStrategy; * @author marhali */ public enum FolderStrategyType { - SINGLE(SingleFolderStrategy.class), - MODULARIZED_LOCALE(ModularLocaleFolderStrategy.class), - MODULARIZED_NAMESPACE(ModularNamespaceFolderStrategy.class); + SINGLE(SingleFolderStrategy.class, false), + MODULARIZED_LOCALE(ModularLocaleFolderStrategy.class, true), + MODULARIZED_NAMESPACE(ModularNamespaceFolderStrategy.class, false); private final Class strategy; + private final boolean namespaceMode; - FolderStrategyType(Class strategy) { + /** + * @param strategy Strategy implementation + * @param namespaceMode Does this strategy use namespaces? + */ + FolderStrategyType(Class strategy, boolean namespaceMode) { this.strategy = strategy; + this.namespaceMode = namespaceMode; } public Class getStrategy() { @@ -38,6 +44,10 @@ public enum FolderStrategyType { throw new NullPointerException(); } + public boolean isNamespaceMode() { + return namespaceMode; + } + public static FolderStrategyType fromIndex(int index) { return values()[index]; } From 676472066485facfd51c681126a080ee0423b11c Mon Sep 17 00:00:00 2001 From: marhali Date: Sun, 10 Apr 2022 20:26:51 +0200 Subject: [PATCH 17/51] fix namespace mode --- src/main/java/de/marhali/easyi18n/model/FolderStrategyType.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/de/marhali/easyi18n/model/FolderStrategyType.java b/src/main/java/de/marhali/easyi18n/model/FolderStrategyType.java index 78d4daf..212538b 100644 --- a/src/main/java/de/marhali/easyi18n/model/FolderStrategyType.java +++ b/src/main/java/de/marhali/easyi18n/model/FolderStrategyType.java @@ -12,7 +12,7 @@ import de.marhali.easyi18n.io.folder.SingleFolderStrategy; public enum FolderStrategyType { SINGLE(SingleFolderStrategy.class, false), MODULARIZED_LOCALE(ModularLocaleFolderStrategy.class, true), - MODULARIZED_NAMESPACE(ModularNamespaceFolderStrategy.class, false); + MODULARIZED_NAMESPACE(ModularNamespaceFolderStrategy.class, true); private final Class strategy; private final boolean namespaceMode; From 8c5efc35737713fa72ccd59de8adf82ec3a1e806 Mon Sep 17 00:00:00 2001 From: marhali Date: Sun, 10 Apr 2022 20:27:12 +0200 Subject: [PATCH 18/51] provide keypath replacement --- .../de/marhali/easyi18n/model/KeyPath.java | 1 + .../easyi18n/model/translation/KeyPath.java | 37 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 src/main/java/de/marhali/easyi18n/model/translation/KeyPath.java diff --git a/src/main/java/de/marhali/easyi18n/model/KeyPath.java b/src/main/java/de/marhali/easyi18n/model/KeyPath.java index 600a66a..17e4d1a 100644 --- a/src/main/java/de/marhali/easyi18n/model/KeyPath.java +++ b/src/main/java/de/marhali/easyi18n/model/KeyPath.java @@ -13,6 +13,7 @@ import java.util.regex.Pattern; * The respective layer (io, presentation) is responsible for using the correct mapping mechanism. * @author marhali */ +@Deprecated public class KeyPath extends ArrayList { public static final String DELIMITER = "."; diff --git a/src/main/java/de/marhali/easyi18n/model/translation/KeyPath.java b/src/main/java/de/marhali/easyi18n/model/translation/KeyPath.java new file mode 100644 index 0000000..5d89f32 --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/model/translation/KeyPath.java @@ -0,0 +1,37 @@ +package de.marhali.easyi18n.model.translation; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +/** + * Represents the absolute key path for a desired translation. + * The key could be based one or many sections. + * Classes implementing this structure need to take care on how to layer translations paths. + * @author marhali + */ +public class KeyPath extends ArrayList { + + public KeyPath() {} + + public KeyPath(@Nullable String... path) { + super.addAll(List.of(path)); + } + + public KeyPath(@NotNull List path) { + super(path); + } + + public KeyPath(@NotNull KeyPath path, @Nullable String... pathToAppend) { + this(path); + super.addAll(List.of(pathToAppend)); + } + + @Override + public String toString() { + // Just a simple array view (e.g. [first, second]) - use KeyPathConverter to properly convert a key path + return super.toString(); + } +} From 567a3385ff3f407dda1c38bc1d6abfc109652af4 Mon Sep 17 00:00:00 2001 From: marhali Date: Sun, 10 Apr 2022 20:27:37 +0200 Subject: [PATCH 19/51] provide translation value replacement --- .../marhali/easyi18n/model/Translation.java | 1 + .../model/translation/TranslationValue.java | 107 ++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 src/main/java/de/marhali/easyi18n/model/translation/TranslationValue.java diff --git a/src/main/java/de/marhali/easyi18n/model/Translation.java b/src/main/java/de/marhali/easyi18n/model/Translation.java index 3237813..cced758 100644 --- a/src/main/java/de/marhali/easyi18n/model/Translation.java +++ b/src/main/java/de/marhali/easyi18n/model/Translation.java @@ -7,6 +7,7 @@ import java.util.HashMap; * This class contains only the translations for this unspecific element. * @author marhali */ +@Deprecated // Replaced by TranslationValue public class Translation extends HashMap { public Translation() { super(); diff --git a/src/main/java/de/marhali/easyi18n/model/translation/TranslationValue.java b/src/main/java/de/marhali/easyi18n/model/translation/TranslationValue.java new file mode 100644 index 0000000..f3e2688 --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/model/translation/TranslationValue.java @@ -0,0 +1,107 @@ +package de.marhali.easyi18n.model.translation; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.Map; + +/** + * Represents the set values behind a specific translation. + * @author marhali + */ +public class TranslationValue { + + private @Nullable String description; + private @NotNull Map values; + private @Nullable Object misc; + + public TranslationValue(@Nullable String description, @NotNull Map values, @Nullable Object misc) { + this.description = description; + this.values = values; + this.misc = misc; + } + + public TranslationValue(@NotNull Map values) { + this(null, values, null); + } + + public TranslationValue(@NotNull String locale, @NotNull String value) { + this(Map.of(locale, value)); + } + + public TranslationValue() { + this(null, new HashMap<>(), null); + } + + /** + * Retrieve additional description for this translation + * @return Description + */ + public @Nullable String getDescription() { + return description; + } + + /** + * Override or set description for this translation + * @param description Description + */ + public void setDescription(@Nullable String description) { + this.description = description; + } + + /** + * Set locale specific values. + * @param values New values + */ + public void setValues(@NotNull Map values) { + this.values = values; + } + + /** + * Overrides or sets a value for a specific locale. + * @param locale Locale type + * @param value New value + */ + public void put(@NotNull String locale, @Nullable String value) { + if(value == null) { // Delete operation + values.remove(locale); + } else { + values.put(locale, value); + } + } + + /** + * Retrieves the associated value for a specific locale + * @param locale Locale type + * @return Value or null if missing + */ + public @Nullable String get(@NotNull String locale) { + return values.get(locale); + } + + /** + * I18n support data + * @return Data + */ + public @Nullable Object getMisc() { + return misc; + } + + /** + * Set or update I18n support data + * @param misc New Data + */ + public void setMisc(@Nullable Object misc) { + this.misc = misc; + } + + @Override + public String toString() { + return "TranslationValue{" + + "description='" + description + '\'' + + ", values=" + values + + ", misc=" + misc + + '}'; + } +} From 0030e77234ac28f1e35db2e2c017dd2779742296 Mon Sep 17 00:00:00 2001 From: marhali Date: Sun, 10 Apr 2022 20:28:25 +0200 Subject: [PATCH 20/51] provide translation replacement --- .../easyi18n/model/KeyedTranslation.java | 1 + .../model/translation/Translation.java | 229 +++++------------- 2 files changed, 55 insertions(+), 175 deletions(-) diff --git a/src/main/java/de/marhali/easyi18n/model/KeyedTranslation.java b/src/main/java/de/marhali/easyi18n/model/KeyedTranslation.java index 04a409f..1e35702 100644 --- a/src/main/java/de/marhali/easyi18n/model/KeyedTranslation.java +++ b/src/main/java/de/marhali/easyi18n/model/KeyedTranslation.java @@ -7,6 +7,7 @@ import org.jetbrains.annotations.Nullable; * I18n translation with associated key path (full-key). * @author marhali */ +@Deprecated // Replaced by Translation public class KeyedTranslation { private @NotNull KeyPath key; diff --git a/src/main/java/de/marhali/easyi18n/model/translation/Translation.java b/src/main/java/de/marhali/easyi18n/model/translation/Translation.java index e5e59cd..5636c86 100644 --- a/src/main/java/de/marhali/easyi18n/model/translation/Translation.java +++ b/src/main/java/de/marhali/easyi18n/model/translation/Translation.java @@ -1,175 +1,54 @@ -package de.marhali.easyi18n.model.translation; - -import de.marhali.easyi18n.model.translation.variant.Plural; - -import de.marhali.easyi18n.model.translation.variant.ContextMap; -import de.marhali.easyi18n.model.translation.variant.LocaleMap; -import de.marhali.easyi18n.model.translation.variant.PluralMap; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * Represents the set values behind a specific translation. - * Consideration is given to context, pluralization and locale. - *
- * Data structure can be imagined like a layered map of: context => plural => locale - * - * @author marhali - */ -public class Translation { - - private @Nullable String description; - private @NotNull ContextMap contexts; - private @Nullable Object misc; - - public Translation(@Nullable String description, @NotNull ContextMap contexts, @Nullable Object misc) { - this.description = description; - this.contexts = contexts; - this.misc = misc; - } - - public Translation() { - this(null, new ContextMap(), null); - } - - /** - * Retrieve additional description for this translation - * @return Description - */ - public @Nullable String getDescription() { - return description; - } - - /** - * Override or set description for this translation - * @param description Description - */ - public void setDescription(@Nullable String description) { - this.description = description; - } - - /** - * Retrieve all contexts for this translation - * @return Map of specified contexts - */ - public @NotNull ContextMap getContexts() { - return contexts; - } - - /** - * Check whether a specific context has been set for this translation - * @param context Context to check - * @return True if context has been configured otherwise false - */ - public boolean hasContext(@NotNull String context) { - return contexts.containsKey(context); - } - - /** - * Retrieve all plurals for a specific context. - * @param context Context to apply - * @return Map of specified plurals - */ - public @Nullable PluralMap getPlurals(@NotNull String context) { - return contexts.get(context); - } - - /** - * Retrieve all locale translations for a specific context & pluralization - * @param context Context to apply - * @param plural Pluralization to apply - * @return Map of specified locales - */ - public @Nullable LocaleMap getLocales(@NotNull String context, @NotNull Plural plural) { - return contexts.getOrDefault(context, new PluralMap()).get(plural); - } - - /** - * Retrieve a specific locale translation for a specific context, pluralization and locale - * @param context Context to apply - * @param plural Pluralization to apply - * @param locale Locale to apply - * @return Translated locale value for the specified variant - */ - public @Nullable String getValue(@NotNull String context, @NotNull Plural plural, @NotNull String locale) { - return contexts.getOrDefault(context, new PluralMap()).getOrDefault(plural, new LocaleMap()).get(locale); - } - - /** - * Override or set context map. - * @param contexts New contexts - */ - public void set(@NotNull ContextMap contexts) { - this.contexts = contexts; - } - - /** - * Override or set a specific context - * @param context Context to use - * @param plurals New plurals map - */ - public void set(@NotNull String context, @NotNull PluralMap plurals) { - contexts.put(context, plurals); - } - - /** - * Override or set locales for a specific context & pluralization - * @param context Context to use - * @param plural Pluralization to use - * @param locales New locales map - */ - public void set(@NotNull String context, @NotNull Plural plural, @NotNull LocaleMap locales) { - PluralMap plurals = getPlurals(context); - - if(plurals == null) { - plurals = new PluralMap(); - } - - plurals.put(plural, locales); - set(context, plurals); - } - - /** - * Override or update a specific translation variant - * @param context Context to use - * @param plural Pluralization to use - * @param locale Locale to use - * @param value New value to set - */ - public void set(@NotNull String context, @NotNull Plural plural, @NotNull String locale, @NotNull String value) { - LocaleMap locales = getLocales(context, plural); - - if(locales == null) { - locales = new LocaleMap(); - } - - locales.put(locale, value); - set(context, plural, locales); - } - - /** - * I18n support data - * @return Data - */ - public @Nullable Object getMisc() { - return misc; - } - - /** - * Set or update I18n support data - * @param misc New Data - */ - public void setMisc(@Nullable Object misc) { - this.misc = misc; - } - - @Override - public String toString() { - return "Translation{" + - "description='" + description + '\'' + - ", contexts=" + contexts + - ", misc=" + misc + - '}'; - } -} +package de.marhali.easyi18n.model.translation; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a translation with defined key and locale values. + * + * @author marhali + */ +public class Translation { + + private final @NotNull KeyPath key; + private @Nullable TranslationValue value; + + /** + * Constructs a new translation instance. + * @param key Absolute key path + * @param value Values to set - nullable to indicate removal + */ + public Translation(@NotNull KeyPath key, @Nullable TranslationValue value) { + this.key = key; + this.value = value; + } + + /** + * @return Absolute key path + */ + public @NotNull KeyPath getKey() { + return key; + } + + /** + * @return values - nullable to indicate removal + */ + public @Nullable TranslationValue getValue() { + return value; + } + + /** + * @param value Values to set - nullable to indicate removal + */ + public void setValue(@Nullable TranslationValue value) { + this.value = value; + } + + @Override + public String toString() { + return "Translation{" + + "key=" + key + + ", value=" + value + + '}'; + } +} \ No newline at end of file From 4b34fe357f868b100af496db6f93bf78fcdf5d46 Mon Sep 17 00:00:00 2001 From: marhali Date: Sun, 10 Apr 2022 20:28:52 +0200 Subject: [PATCH 21/51] remove legacy tests --- .../easyi18n/LegacyKeyPathConverterTest.java | 54 +++++++++++++++++++ .../de/marhali/easyi18n/TranslationTest.java | 43 --------------- 2 files changed, 54 insertions(+), 43 deletions(-) create mode 100644 src/test/java/de/marhali/easyi18n/LegacyKeyPathConverterTest.java delete mode 100644 src/test/java/de/marhali/easyi18n/TranslationTest.java diff --git a/src/test/java/de/marhali/easyi18n/LegacyKeyPathConverterTest.java b/src/test/java/de/marhali/easyi18n/LegacyKeyPathConverterTest.java new file mode 100644 index 0000000..57ba445 --- /dev/null +++ b/src/test/java/de/marhali/easyi18n/LegacyKeyPathConverterTest.java @@ -0,0 +1,54 @@ +package de.marhali.easyi18n; + +import de.marhali.easyi18n.model.KeyPath; +import de.marhali.easyi18n.model.KeyPathConverter; + +import org.junit.Assert; +import org.junit.Test; + +/** + * Unit tests for {@link KeyPathConverter}. + * @author marhali + */ +@Deprecated +public class LegacyKeyPathConverterTest { + + private final KeyPathConverter deepMapper = new KeyPathConverter(true); + private final KeyPathConverter flatMapper = new KeyPathConverter(false); + + @Test + public void testNestedConcat() { + Assert.assertEquals("first\\\\.section.second.third", + deepMapper.concat(KeyPath.of("first.section", "second", "third"))); + + Assert.assertEquals("first.second.third", + deepMapper.concat(KeyPath.of("first", "second", "third"))); + } + + @Test + public void testNestedSplit() { + Assert.assertEquals(KeyPath.of("first.section", "second", "third"), + deepMapper.split("first\\\\.section.second.third")); + + Assert.assertEquals(KeyPath.of("first", "second", "third"), + deepMapper.split("first.second.third")); + } + + @Test + public void testNonNestedConcat() { + Assert.assertEquals("flat.map\\\\.deeper", + flatMapper.concat(KeyPath.of("flat.map", "deeper"))); + + Assert.assertEquals("flat.map.keys", + flatMapper.concat(KeyPath.of("flat.map.keys"))); + } + + @Test + public void testNonNestedSplit() { + Assert.assertEquals(KeyPath.of("flat.keys.with", "deep.section"), + flatMapper.split("flat.keys.with\\\\.deep.section")); + + Assert.assertEquals(KeyPath.of("flat.keys.only"), + flatMapper.split("flat.keys.only")); + } +} \ No newline at end of file diff --git a/src/test/java/de/marhali/easyi18n/TranslationTest.java b/src/test/java/de/marhali/easyi18n/TranslationTest.java deleted file mode 100644 index 7e8f9ad..0000000 --- a/src/test/java/de/marhali/easyi18n/TranslationTest.java +++ /dev/null @@ -1,43 +0,0 @@ -package de.marhali.easyi18n; - -import de.marhali.easyi18n.model.translation.variant.Plural; -import de.marhali.easyi18n.model.translation.variant.ContextMap; -import de.marhali.easyi18n.model.translation.variant.LocaleMap; -import de.marhali.easyi18n.model.translation.Translation; - -import org.junit.Assert; -import org.junit.Test; - -import java.util.Map; - -/** - * Unit tests for {@link Translation}. - * @author marhali - */ -public class TranslationTest { - - @Test - public void add() { - Translation value = new Translation(); - value.set(ContextMap.DEFAULT, Plural.DEFAULT, "en", "hello"); - Assert.assertEquals(value.getValue(ContextMap.DEFAULT, Plural.DEFAULT, "en"), "hello"); - } - - @Test - public void override() { - Translation value = new Translation(); - value.set(ContextMap.DEFAULT, Plural.DEFAULT, new LocaleMap(Map.of("en", "hello", "de", "hallo"))); - value.set(ContextMap.DEFAULT, Plural.DEFAULT, "en", "new hello"); - Assert.assertEquals(value.getValue(ContextMap.DEFAULT, Plural.DEFAULT, "en"), "new hello"); - Assert.assertEquals(value.getValue(ContextMap.DEFAULT, Plural.DEFAULT, "de"), "hallo"); - } - - @Test - public void plurals() { - Translation value = new Translation(); - value.set(ContextMap.DEFAULT, Plural.ONE, "en", "boyfriend"); - value.set(ContextMap.DEFAULT, Plural.MANY, "en", "boyfriends"); - Assert.assertEquals(value.getValue(ContextMap.DEFAULT, Plural.ONE, "en"), "boyfriend"); - Assert.assertEquals(value.getValue(ContextMap.DEFAULT, Plural.MANY, "en"), "boyfriends"); - } -} From 9d5be9caa09720f546a8cdd4660b487e2bdfab72 Mon Sep 17 00:00:00 2001 From: marhali Date: Sun, 10 Apr 2022 20:29:13 +0200 Subject: [PATCH 22/51] partial work on KeyPathConverter replacement --- .../easyi18n/model/KeyPathConverter.java | 1 + .../easyi18n/util/KeyPathConverter.java | 145 ++++++++++++++++++ .../easyi18n/KeyPathConverterTest.java | 145 ++++++++++++++---- 3 files changed, 265 insertions(+), 26 deletions(-) create mode 100644 src/main/java/de/marhali/easyi18n/util/KeyPathConverter.java diff --git a/src/main/java/de/marhali/easyi18n/model/KeyPathConverter.java b/src/main/java/de/marhali/easyi18n/model/KeyPathConverter.java index cfb9d26..1bc5fcd 100644 --- a/src/main/java/de/marhali/easyi18n/model/KeyPathConverter.java +++ b/src/main/java/de/marhali/easyi18n/model/KeyPathConverter.java @@ -13,6 +13,7 @@ import java.util.regex.Pattern; * If nesting is enabled the delimiter within a section is escaped otherwise the delimiter between the key sections. * @author marhali */ +@Deprecated // Replacement moved to utils public class KeyPathConverter { private final boolean nestKeys; diff --git a/src/main/java/de/marhali/easyi18n/util/KeyPathConverter.java b/src/main/java/de/marhali/easyi18n/util/KeyPathConverter.java new file mode 100644 index 0000000..112e119 --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/util/KeyPathConverter.java @@ -0,0 +1,145 @@ +package de.marhali.easyi18n.util; + +import de.marhali.easyi18n.model.translation.KeyPath; +import de.marhali.easyi18n.settings.ProjectSettings; + +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; +import java.util.regex.Pattern; + +/** + * Stateful utility to transform absolute translation keys into their character literal representation and backwards. + * @author marhali + */ +public class KeyPathConverter { + + private final ProjectSettings settings; + + /** + * Constructs a new converter instance + * @param settings Delimiter configuration + */ + public KeyPathConverter(ProjectSettings settings) { + this.settings = settings; + } + + /** + * Transform to character literal representation + * @param path Absolute key path + * @return Character literal + */ + public @NotNull String toString(@NotNull KeyPath path) { + StringBuilder builder = new StringBuilder(); + + for(int i = 0; i < path.size(); i++) { + if(i > 0) { // Delimiters + if(i == 1 && settings.getFolderStrategy().isNamespaceMode() && settings.getNamespaceDelimiter() != null) { + builder.append(quoteDelimiter(settings.getNamespaceDelimiter())); + } else { + builder.append(quoteDelimiter(settings.getSectionDelimiter())); + } + } + + // Section content + builder.append(quoteSection(path.get(i))); + } + + return builder.toString(); + } + + /** + * Splits provided character literal into key path sections. + * If namespace mode is activated and none was provided, the default namespace will be added. + * @return Layered key path sections + */ + public @NotNull KeyPath fromString(@NotNull String literalPath) { + KeyPath path = new KeyPath(); + + int i = 0; + for(String section : literalPath.split(getSplitRegex())) { + + // Missing namespace + if(i == 0 && settings.getFolderStrategy().isNamespaceMode() && hasDefaultNamespace()) { + if(section.length() == literalPath.length() || !String.valueOf(literalPath.charAt(section.length())).equals(settings.getNamespaceDelimiter())) { + path.add(settings.getDefaultNamespace()); + } + } + + path.add(unquoteSection(section)); + + i++; + } + + return path; + } + + @Override + public String toString() { + return "KeyPathConverter{" + + "settings=" + settings + + '}'; + } + + /* + * INTERNAL METHODS + */ + + private boolean hasDefaultNamespace() { + return settings.getDefaultNamespace() != null && !settings.getDefaultNamespace().isEmpty(); + } + + private String getSplitRegex() { + return settings.isNestedKeys() + ? ("(? Date: Sun, 10 Apr 2022 21:39:56 +0200 Subject: [PATCH 23/51] finalize KeyPathConverter --- .../easyi18n/util/KeyPathConverter.java | 21 ++++++++++-------- .../easyi18n/KeyPathConverterTest.java | 22 ++++++++++++++++--- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/main/java/de/marhali/easyi18n/util/KeyPathConverter.java b/src/main/java/de/marhali/easyi18n/util/KeyPathConverter.java index 112e119..cde4cd2 100644 --- a/src/main/java/de/marhali/easyi18n/util/KeyPathConverter.java +++ b/src/main/java/de/marhali/easyi18n/util/KeyPathConverter.java @@ -61,7 +61,8 @@ public class KeyPathConverter { // Missing namespace if(i == 0 && settings.getFolderStrategy().isNamespaceMode() && hasDefaultNamespace()) { - if(section.length() == literalPath.length() || !String.valueOf(literalPath.charAt(section.length())).equals(settings.getNamespaceDelimiter())) { + String namespaceDelim = (settings.isNestedKeys() ? "" : "\\") + settings.getNamespaceDelimiter(); + if(section.length() == literalPath.length() || !literalPath.substring(section.length()).startsWith(namespaceDelim)) { path.add(settings.getDefaultNamespace()); } } @@ -102,7 +103,7 @@ public class KeyPathConverter { builder.append(Pattern.quote(settings.getSectionDelimiter())); // Add optional namespace delimiter if present - if(hasDefaultNamespace()) { + if(settings.getNamespaceDelimiter() != null && !settings.getNamespaceDelimiter().isEmpty()) { builder.append("|"); builder.append(Pattern.quote(Objects.requireNonNull(settings.getNamespaceDelimiter()))); } @@ -115,25 +116,27 @@ public class KeyPathConverter { * Securely escape found delimiters inside provided section according to the configured policy. */ private String quoteSection(String section) { + String quoted = section; if(!settings.isNestedKeys()) { - return section; + return quoted; } if(hasDefaultNamespace()) { - section = section.replace(settings.getNamespaceDelimiter(), "\\" + settings.getNamespaceDelimiter()); + quoted = quoted.replace(settings.getNamespaceDelimiter(), "\\" + settings.getNamespaceDelimiter()); } - section = section.replace(settings.getSectionDelimiter(), "\\" + settings.getSectionDelimiter()); - return section; + quoted = quoted.replace(settings.getSectionDelimiter(), "\\" + settings.getSectionDelimiter()); + return quoted; } private String unquoteSection(String section) { + String unquoted = section; if(hasDefaultNamespace()) { - section = section.replace("\\" + settings.getNamespaceDelimiter(), settings.getNamespaceDelimiter()); + unquoted = unquoted.replace("\\" + settings.getNamespaceDelimiter(), settings.getNamespaceDelimiter()); } - section = section.replace("\\" + settings.getSectionDelimiter(), settings.getSectionDelimiter()); - return section; + unquoted = unquoted.replace("\\" + settings.getSectionDelimiter(), settings.getSectionDelimiter()); + return unquoted; } /** diff --git a/src/test/java/de/marhali/easyi18n/KeyPathConverterTest.java b/src/test/java/de/marhali/easyi18n/KeyPathConverterTest.java index 7dfd845..9718215 100644 --- a/src/test/java/de/marhali/easyi18n/KeyPathConverterTest.java +++ b/src/test/java/de/marhali/easyi18n/KeyPathConverterTest.java @@ -11,13 +11,30 @@ import org.jetbrains.annotations.Nullable; import org.junit.Assert; import org.junit.Test; - /** * Unit tests for {@link KeyPathConverter}. * @author marhali */ public class KeyPathConverterTest { + @Test + public void noNamespaceDelimiter() { + KeyPathConverter converter = getConverter(FolderStrategyType.MODULARIZED_NAMESPACE, null, ".", null, true); + + Assert.assertEquals(new KeyPath("username"), converter.fromString("username")); + Assert.assertEquals(new KeyPath("username:nested"), converter.fromString("username:nested")); + Assert.assertEquals(new KeyPath("username:nested", "leaf"), converter.fromString("username:nested.leaf")); + } + + @Test + public void emptyDefaultNamespace() { + KeyPathConverter converter = getConverter(FolderStrategyType.MODULARIZED_NAMESPACE, ":", ".", null, true); + + Assert.assertEquals(new KeyPath("username"), converter.fromString("username")); + Assert.assertEquals(new KeyPath("username", "nested"), converter.fromString("username:nested")); + Assert.assertEquals(new KeyPath("username", "nested", "leaf"), converter.fromString("username:nested.leaf")); + } + @Test public void nonNestedSingle() { KeyPathConverter converter = getConverter(FolderStrategyType.SINGLE, null, ".", null, false); @@ -39,8 +56,7 @@ public class KeyPathConverterTest { Assert.assertEquals("username.title\\:concat.leaf\\.node", converter.toString(new KeyPath("username.title", "concat.leaf", "node"))); Assert.assertEquals(new KeyPath("common", "username"), converter.fromString("username")); - // TODO: problem here - Assert.assertEquals(new KeyPath("username.title", "concat.leaf", "node"), converter.fromString("username.title\\:concat\\.leaf.node")); + Assert.assertEquals(new KeyPath("username.title", "concat", "leaf.node"), converter.fromString("username.title\\:concat\\.leaf.node")); } @Test From c9ef651673be833fc43e84f3d07e536888758b57 Mon Sep 17 00:00:00 2001 From: marhali Date: Mon, 11 Apr 2022 19:14:07 +0200 Subject: [PATCH 24/51] rework translation dialogs --- .../de/marhali/easyi18n/action/AddAction.java | 10 +- .../de/marhali/easyi18n/dialog/AddDialog.java | 122 +++++----------- .../marhali/easyi18n/dialog/EditDialog.java | 109 ++++----------- .../easyi18n/dialog/TranslationDialog.java | 132 ++++++++++++++++++ 4 files changed, 204 insertions(+), 169 deletions(-) create mode 100644 src/main/java/de/marhali/easyi18n/dialog/TranslationDialog.java diff --git a/src/main/java/de/marhali/easyi18n/action/AddAction.java b/src/main/java/de/marhali/easyi18n/action/AddAction.java index b356b9a..05be4f1 100644 --- a/src/main/java/de/marhali/easyi18n/action/AddAction.java +++ b/src/main/java/de/marhali/easyi18n/action/AddAction.java @@ -6,10 +6,10 @@ import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.project.Project; import com.intellij.ui.content.Content; -import de.marhali.easyi18n.model.KeyPath; -import de.marhali.easyi18n.model.KeyPathConverter; -import de.marhali.easyi18n.service.WindowManager; import de.marhali.easyi18n.dialog.AddDialog; +import de.marhali.easyi18n.model.translation.KeyPath; +import de.marhali.easyi18n.service.WindowManager; +import de.marhali.easyi18n.util.KeyPathConverter; import de.marhali.easyi18n.util.TreeUtil; import org.jetbrains.annotations.NotNull; @@ -32,7 +32,7 @@ public class AddAction extends AnAction { @Override public void actionPerformed(@NotNull AnActionEvent e) { - new AddDialog(Objects.requireNonNull(e.getProject()), detectPreKey(e.getProject())).showAndHandle(); + new AddDialog(Objects.requireNonNull(e.getProject()), detectPreKey(e.getProject()), null).showAndHandle(); } private @Nullable KeyPath detectPreKey(@NotNull Project project) { @@ -58,7 +58,7 @@ public class AddAction extends AnAction { if(row >= 0) { String path = String.valueOf(window.getTableView().getTable().getValueAt(row, 0)); - return converter.split(path); + return converter.fromString(path); } } diff --git a/src/main/java/de/marhali/easyi18n/dialog/AddDialog.java b/src/main/java/de/marhali/easyi18n/dialog/AddDialog.java index dd886b0..0998611 100644 --- a/src/main/java/de/marhali/easyi18n/dialog/AddDialog.java +++ b/src/main/java/de/marhali/easyi18n/dialog/AddDialog.java @@ -3,112 +3,64 @@ package de.marhali.easyi18n.dialog; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogBuilder; import com.intellij.openapi.ui.DialogWrapper; -import com.intellij.ui.components.JBLabel; -import com.intellij.ui.components.JBScrollPane; -import com.intellij.ui.components.JBTextField; import de.marhali.easyi18n.InstanceManager; -import de.marhali.easyi18n.model.*; +import de.marhali.easyi18n.model.TranslationCreate; +import de.marhali.easyi18n.model.translation.KeyPath; +import de.marhali.easyi18n.model.translation.Translation; +import de.marhali.easyi18n.model.translation.TranslationValue; +import de.marhali.easyi18n.settings.ProjectSettingsService; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; -import javax.swing.border.EtchedBorder; -import java.awt.*; -import java.util.HashMap; -import java.util.Map; -import java.util.ResourceBundle; /** - * Create translation dialog. + * Dialog to create a new translation with all associated locale values. + * Supports optional prefill technique for translation key or locale value. * @author marhali */ -public class AddDialog { +public class AddDialog extends TranslationDialog { - private final @NotNull Project project; - private final @NotNull KeyPathConverter converter; - - private @NotNull KeyPath preKey; - - private JBTextField keyTextField; - private Map valueTextFields; - - public AddDialog(@NotNull Project project, @Nullable KeyPath preKey) { - this(project); - this.preKey = preKey == null ? new KeyPath() : preKey; + /** + * Constructs a new create dialog with prefilled fields + * @param project Opened project + * @param prefillKey Prefill translation key + * @param prefillLocale Prefill preview locale value + */ + public AddDialog(@NotNull Project project, @Nullable KeyPath prefillKey, @Nullable String prefillLocale) { + super(project, new Translation(prefillKey != null ? prefillKey : new KeyPath(), + prefillLocale != null + ? new TranslationValue(ProjectSettingsService.get(project).getState().getPreviewLocale(), prefillLocale) + : null) + ); } + /** + * Constructs a new create dialog without prefilled fields. + * @param project Opened project + */ public AddDialog(@NotNull Project project) { - this.project = project; - this.converter = new KeyPathConverter(project); - this.preKey = new KeyPath(); + this(project, new KeyPath(), ""); } - public void showAndHandle() { - int code = prepare().show(); - - if(code == DialogWrapper.OK_EXIT_CODE) { - saveTranslation(); - } - } - - private void saveTranslation() { - Translation translation = new Translation(); - - valueTextFields.forEach((k, v) -> { - if(!v.getText().isEmpty()) { - translation.put(k, v.getText()); - } - }); - - KeyedTranslation keyedTranslation = new KeyedTranslation(converter.split(keyTextField.getText()), translation); - TranslationCreate creation = new TranslationCreate(keyedTranslation); - InstanceManager.get(project).processUpdate(creation); - } - - private DialogBuilder prepare() { - JPanel rootPanel = new JPanel(); - rootPanel.setLayout(new BoxLayout(rootPanel, BoxLayout.PAGE_AXIS)); - - JPanel keyPanel = new JPanel(new GridLayout(0, 1, 2, 2)); - JBLabel keyLabel = new JBLabel(ResourceBundle.getBundle("messages").getString("translation.key")); - keyTextField = new JBTextField(this.converter.concat(this.preKey)); - keyLabel.setLabelFor(keyTextField); - keyPanel.add(keyLabel); - keyPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0)); - keyPanel.add(keyTextField); - rootPanel.add(keyPanel); - - if(!this.preKey.isEmpty()) { // Add delimiter if pre key is defined - keyTextField.setText(keyTextField.getText() + KeyPath.DELIMITER); - } - - JPanel valuePanel = new JPanel(new GridLayout(0, 1, 2, 2)); - valueTextFields = new HashMap<>(); - - for(String locale : InstanceManager.get(project).store().getData().getLocales()) { - JBLabel localeLabel = new JBLabel(locale); - JBTextField localeText = new JBTextField(); - localeLabel.setLabelFor(localeText); - - valuePanel.add(localeLabel); - valuePanel.add(localeText); - valueTextFields.put(locale, localeText); - } - - JBScrollPane valuePane = new JBScrollPane(valuePanel); - valuePane.setBorder(BorderFactory.createTitledBorder(new EtchedBorder(), - ResourceBundle.getBundle("messages").getString("translation.locales"))); - rootPanel.add(valuePane); + @Override + protected @NotNull DialogBuilder configure(@NotNull JComponent centerPanel) { DialogBuilder builder = new DialogBuilder(); - builder.setTitle(ResourceBundle.getBundle("messages").getString("action.add")); + builder.setTitle(bundle.getString("action.add")); builder.removeAllActions(); builder.addOkAction(); builder.addCancelAction(); - builder.setCenterPanel(rootPanel); - + builder.setCenterPanel(centerPanel); return builder; } -} \ No newline at end of file + + @Override + protected void handleExit(int exitCode) { + if(exitCode == DialogWrapper.OK_EXIT_CODE) { + InstanceManager.get(project).processUpdate(new TranslationCreate(getState())); + } + } +} diff --git a/src/main/java/de/marhali/easyi18n/dialog/EditDialog.java b/src/main/java/de/marhali/easyi18n/dialog/EditDialog.java index cc47e27..97cc5f5 100644 --- a/src/main/java/de/marhali/easyi18n/dialog/EditDialog.java +++ b/src/main/java/de/marhali/easyi18n/dialog/EditDialog.java @@ -3,102 +3,53 @@ package de.marhali.easyi18n.dialog; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogBuilder; import com.intellij.openapi.ui.DialogWrapper; -import com.intellij.ui.components.JBLabel; -import com.intellij.ui.components.JBScrollPane; -import com.intellij.ui.components.JBTextField; import de.marhali.easyi18n.InstanceManager; -import de.marhali.easyi18n.model.*; import de.marhali.easyi18n.dialog.descriptor.DeleteActionDescriptor; +import de.marhali.easyi18n.model.TranslationDelete; +import de.marhali.easyi18n.model.TranslationUpdate; +import de.marhali.easyi18n.model.translation.Translation; + +import org.jetbrains.annotations.NotNull; import javax.swing.*; -import javax.swing.border.EtchedBorder; -import java.awt.*; -import java.util.HashMap; -import java.util.Map; -import java.util.ResourceBundle; /** - * Edit translation dialog. + * Dialog to edit or delete an existing translation. * @author marhali */ -public class EditDialog { +public class EditDialog extends TranslationDialog { - private final Project project; - private final KeyPathConverter converter; - - private final KeyedTranslation origin; - - private JBTextField keyTextField; - private Map valueTextFields; - - public EditDialog(Project project, KeyedTranslation origin) { - this.project = project; - this.converter = new KeyPathConverter(project); - this.origin = origin; + /** + * Constructs a new edit dialog with the provided translation + * @param project Opened project + * @param origin Translation to edit + */ + public EditDialog(@NotNull Project project, @NotNull Translation origin) { + super(project, origin); } - public void showAndHandle() { - int code = prepare().show(); - - if(code == DialogWrapper.OK_EXIT_CODE) { // Edit - InstanceManager.get(project).processUpdate(new TranslationUpdate(origin, getChanges())); - } else if(code == DeleteActionDescriptor.EXIT_CODE) { // Delete - InstanceManager.get(project).processUpdate(new TranslationDelete(origin)); - } - } - - private KeyedTranslation getChanges() { - Translation translation = new Translation(); - - valueTextFields.forEach((k, v) -> { - if(!v.getText().isEmpty()) { - translation.put(k, v.getText()); - } - }); - - return new KeyedTranslation(converter.split(keyTextField.getText()), translation); - } - - private DialogBuilder prepare() { - JPanel rootPanel = new JPanel(); - rootPanel.setLayout(new BoxLayout(rootPanel, BoxLayout.PAGE_AXIS)); - - JPanel keyPanel = new JPanel(new GridLayout(0, 1, 2,2)); - JBLabel keyLabel = new JBLabel(ResourceBundle.getBundle("messages").getString("translation.key")); - keyTextField = new JBTextField(this.converter.concat(this.origin.getKey())); - keyLabel.setLabelFor(keyTextField); - keyPanel.add(keyLabel); - keyPanel.add(keyTextField); - keyPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0)); - rootPanel.add(keyPanel); - - JPanel valuePanel = new JPanel(new GridLayout(0, 1, 2, 2)); - valueTextFields = new HashMap<>(); - - for(String locale : InstanceManager.get(project).store().getData().getLocales()) { - JBLabel localeLabel = new JBLabel(locale); - JBTextField localeText = new JBTextField(this.origin.getTranslation().get(locale)); - localeLabel.setLabelFor(localeText); - - valuePanel.add(localeLabel); - valuePanel.add(localeText); - valueTextFields.put(locale, localeText); - } - - JBScrollPane valuePane = new JBScrollPane(valuePanel); - valuePane.setBorder(BorderFactory.createTitledBorder(new EtchedBorder(), - ResourceBundle.getBundle("messages").getString("translation.locales"))); - rootPanel.add(valuePane); - + @Override + protected @NotNull DialogBuilder configure(@NotNull JComponent centerPanel) { DialogBuilder builder = new DialogBuilder(); - builder.setTitle(ResourceBundle.getBundle("messages").getString("action.edit")); + builder.setTitle(bundle.getString("action.edit")); builder.removeAllActions(); builder.addCancelAction(); builder.addActionDescriptor(new DeleteActionDescriptor()); builder.addOkAction(); - builder.setCenterPanel(rootPanel); - + builder.setCenterPanel(centerPanel); return builder; } + + @Override + protected void handleExit(int exitCode) { + switch (exitCode) { + case DialogWrapper.OK_EXIT_CODE: + InstanceManager.get(project).processUpdate(new TranslationUpdate(origin, getState())); + break; + case DeleteActionDescriptor.EXIT_CODE: + InstanceManager.get(project).processUpdate(new TranslationDelete(origin)); + break; + } + } } \ No newline at end of file diff --git a/src/main/java/de/marhali/easyi18n/dialog/TranslationDialog.java b/src/main/java/de/marhali/easyi18n/dialog/TranslationDialog.java new file mode 100644 index 0000000..01cacd7 --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/dialog/TranslationDialog.java @@ -0,0 +1,132 @@ +package de.marhali.easyi18n.dialog; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogBuilder; +import com.intellij.ui.components.JBScrollPane; +import com.intellij.ui.components.JBTextField; +import com.intellij.util.ui.FormBuilder; + +import de.marhali.easyi18n.InstanceManager; +import de.marhali.easyi18n.model.translation.KeyPath; +import de.marhali.easyi18n.model.translation.Translation; +import de.marhali.easyi18n.model.translation.TranslationValue; +import de.marhali.easyi18n.settings.ProjectSettings; +import de.marhali.easyi18n.settings.ProjectSettingsService; +import de.marhali.easyi18n.util.KeyPathConverter; + +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import javax.swing.border.EtchedBorder; +import java.awt.*; +import java.util.HashMap; +import java.util.Map; +import java.util.ResourceBundle; + +/** + * Base for add and edit translation dialogs. + * @author marhali + */ +abstract class TranslationDialog { + + protected static final ResourceBundle bundle = ResourceBundle.getBundle("messages"); + + protected final @NotNull Project project; + protected final @NotNull ProjectSettings settings; + protected final @NotNull KeyPathConverter converter; + protected final @NotNull Translation origin; + + protected final JTextField keyField; + protected final JTextField descriptionField; + protected final Map localeValueFields; + + /** + * Constructs a new translation dialog. + * @param project Opened project + * @param origin Prefill translation + */ + protected TranslationDialog(@NotNull Project project, @NotNull Translation origin) { + this.project = project; + this.settings = ProjectSettingsService.get(project).getState(); + this.converter = new KeyPathConverter(settings); + this.origin = origin; + + // Fields + TranslationValue value = origin.getValue(); + + this.keyField = new JBTextField(converter.toString(origin.getKey())); + this.descriptionField = new JBTextField(value != null ? value.getDescription() : null); + this.localeValueFields = new HashMap<>(); + + for(String locale : InstanceManager.get(project).store().getData().getLocales()) { + localeValueFields.put(locale, new JBTextField(value != null ? value.get(locale) : null)); + } + } + + /** + * Implementation needs to configure the dialog. E.g. title, actions, ... + * The implementation needs to set the provided centerPanel as the view panel. + * @param centerPanel GUI to set on the dialog builder + * @return configured dialog builder + */ + protected abstract @NotNull DialogBuilder configure(@NotNull JComponent centerPanel); + + /** + * Implementation needs to handle exit + * @param exitCode See {@link com.intellij.openapi.ui.DialogWrapper} for exit codes + */ + protected abstract void handleExit(int exitCode); + + /** + * Opens the translation modal and applies the appropriate logic on modal close. + * Internally, the {@link #handleExit(int)} method will be called to determine finalization logic. + */ + public void showAndHandle() { + int exitCode = createDialog().show(); + handleExit(exitCode); + } + + /** + * Retrieve current modal state. + * @return Translation + */ + protected @NotNull Translation getState() { + KeyPath key = converter.fromString(keyField.getText()); + TranslationValue value = new TranslationValue(); + + value.setDescription(descriptionField.getText()); + + for(Map.Entry entry : localeValueFields.entrySet()) { + value.put(entry.getKey(), entry.getValue().getText()); + } + + return new Translation(key, value); + } + + private DialogBuilder createDialog() { + JPanel panel = FormBuilder.createFormBuilder() + .addLabeledComponent(bundle.getString("translation.key"), keyField, true) + .addLabeledComponent(bundle.getString("translation.description"), descriptionField, 6, true) + .addComponent(createLocalesPanel(), 12) + .getPanel(); + + panel.setMinimumSize(new Dimension(200, 150)); + + return configure(panel); + } + + private JComponent createLocalesPanel() { + FormBuilder builder = FormBuilder.createFormBuilder(); + + for(Map.Entry localeEntry : localeValueFields.entrySet()) { + builder.addLabeledComponent(localeEntry.getKey(), localeEntry.getValue(), 6, true); + } + + JScrollPane scrollPane = new JBScrollPane(builder.getPanel()); + + scrollPane.setBorder(BorderFactory.createTitledBorder( + new EtchedBorder(), bundle.getString("translation.locales"))); + + return scrollPane; + } +} \ No newline at end of file From 024fde405c94c06497074af84c2a14fc87af99a5 Mon Sep 17 00:00:00 2001 From: marhali Date: Mon, 11 Apr 2022 19:14:33 +0200 Subject: [PATCH 25/51] update changelog --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d159c2d..9d18be1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,16 @@ # easy-i18n Changelog ## [Unreleased] +### BREAKING CHANGES +- Configuration rework. Existing settings will be lost and must be configured via the new configuration page + ### Added +- Key delimiters (namespace / section) can be configured - Individual icon to tool-window +### Changed +- Moved configuration dialog into own page inside IDE Settings + ### Fixed - AlreadyDisposedException on FileChangeListener after project dispose From aef851d462375eb6e4985c042031d4633765bf63 Mon Sep 17 00:00:00 2001 From: marhali Date: Mon, 11 Apr 2022 19:14:55 +0200 Subject: [PATCH 26/51] add description translation --- src/main/resources/messages.properties | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties index 5d3cc88..c3eb368 100644 --- a/src/main/resources/messages.properties +++ b/src/main/resources/messages.properties @@ -11,6 +11,7 @@ action.settings=Settings action.search=Search... action.delete=Delete translation.key=Key +translation.description=Description translation.locales=Locales # Settings settings.hint.text=Project-specific configuration. For an easy start, you can use one of the existing presets. From 4333e2a996388f82468e42c8397b0011845ba9ff Mon Sep 17 00:00:00 2001 From: marhali Date: Mon, 11 Apr 2022 19:15:18 +0200 Subject: [PATCH 27/51] remove legacy structure --- .../easyi18n/model/KeyedTranslation.java | 44 --------------- .../easyi18n/LegacyKeyPathConverterTest.java | 54 ------------------- 2 files changed, 98 deletions(-) delete mode 100644 src/main/java/de/marhali/easyi18n/model/KeyedTranslation.java delete mode 100644 src/test/java/de/marhali/easyi18n/LegacyKeyPathConverterTest.java diff --git a/src/main/java/de/marhali/easyi18n/model/KeyedTranslation.java b/src/main/java/de/marhali/easyi18n/model/KeyedTranslation.java deleted file mode 100644 index 1e35702..0000000 --- a/src/main/java/de/marhali/easyi18n/model/KeyedTranslation.java +++ /dev/null @@ -1,44 +0,0 @@ -package de.marhali.easyi18n.model; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * I18n translation with associated key path (full-key). - * @author marhali - */ -@Deprecated // Replaced by Translation -public class KeyedTranslation { - - private @NotNull KeyPath key; - private @Nullable Translation translation; - - public KeyedTranslation(@NotNull KeyPath key, @Nullable Translation translation) { - this.key = key; - this.translation = translation; - } - - public KeyPath getKey() { - return key; - } - - public void setKey(KeyPath key) { - this.key = key; - } - - public @Nullable Translation getTranslation() { - return translation; - } - - public void setTranslation(@NotNull Translation translation) { - this.translation = translation; - } - - @Override - public String toString() { - return "KeyedTranslation{" + - "key='" + key + '\'' + - ", translation=" + translation + - '}'; - } -} \ No newline at end of file diff --git a/src/test/java/de/marhali/easyi18n/LegacyKeyPathConverterTest.java b/src/test/java/de/marhali/easyi18n/LegacyKeyPathConverterTest.java deleted file mode 100644 index 57ba445..0000000 --- a/src/test/java/de/marhali/easyi18n/LegacyKeyPathConverterTest.java +++ /dev/null @@ -1,54 +0,0 @@ -package de.marhali.easyi18n; - -import de.marhali.easyi18n.model.KeyPath; -import de.marhali.easyi18n.model.KeyPathConverter; - -import org.junit.Assert; -import org.junit.Test; - -/** - * Unit tests for {@link KeyPathConverter}. - * @author marhali - */ -@Deprecated -public class LegacyKeyPathConverterTest { - - private final KeyPathConverter deepMapper = new KeyPathConverter(true); - private final KeyPathConverter flatMapper = new KeyPathConverter(false); - - @Test - public void testNestedConcat() { - Assert.assertEquals("first\\\\.section.second.third", - deepMapper.concat(KeyPath.of("first.section", "second", "third"))); - - Assert.assertEquals("first.second.third", - deepMapper.concat(KeyPath.of("first", "second", "third"))); - } - - @Test - public void testNestedSplit() { - Assert.assertEquals(KeyPath.of("first.section", "second", "third"), - deepMapper.split("first\\\\.section.second.third")); - - Assert.assertEquals(KeyPath.of("first", "second", "third"), - deepMapper.split("first.second.third")); - } - - @Test - public void testNonNestedConcat() { - Assert.assertEquals("flat.map\\\\.deeper", - flatMapper.concat(KeyPath.of("flat.map", "deeper"))); - - Assert.assertEquals("flat.map.keys", - flatMapper.concat(KeyPath.of("flat.map.keys"))); - } - - @Test - public void testNonNestedSplit() { - Assert.assertEquals(KeyPath.of("flat.keys.with", "deep.section"), - flatMapper.split("flat.keys.with\\\\.deep.section")); - - Assert.assertEquals(KeyPath.of("flat.keys.only"), - flatMapper.split("flat.keys.only")); - } -} \ No newline at end of file From c906f144fab0af42f4a27a8e78d1aeea572d19a8 Mon Sep 17 00:00:00 2001 From: marhali Date: Mon, 11 Apr 2022 19:19:39 +0200 Subject: [PATCH 28/51] remove legacy structure --- .../easyi18n/model/KeyPathConverter.java | 70 ------------------- .../easyi18n/model/translation/KeyPath.java | 37 ---------- .../model/translation/Translation.java | 54 -------------- 3 files changed, 161 deletions(-) delete mode 100644 src/main/java/de/marhali/easyi18n/model/KeyPathConverter.java delete mode 100644 src/main/java/de/marhali/easyi18n/model/translation/KeyPath.java delete mode 100644 src/main/java/de/marhali/easyi18n/model/translation/Translation.java diff --git a/src/main/java/de/marhali/easyi18n/model/KeyPathConverter.java b/src/main/java/de/marhali/easyi18n/model/KeyPathConverter.java deleted file mode 100644 index 1bc5fcd..0000000 --- a/src/main/java/de/marhali/easyi18n/model/KeyPathConverter.java +++ /dev/null @@ -1,70 +0,0 @@ -package de.marhali.easyi18n.model; - -import com.intellij.openapi.project.Project; - -import de.marhali.easyi18n.settings.ProjectSettingsService; - -import org.jetbrains.annotations.NotNull; - -import java.util.regex.Pattern; - -/** - * Responsible for mapping {@link KeyPath} into single string and backwards. - * If nesting is enabled the delimiter within a section is escaped otherwise the delimiter between the key sections. - * @author marhali - */ -@Deprecated // Replacement moved to utils -public class KeyPathConverter { - - private final boolean nestKeys; - - public KeyPathConverter(boolean nestKeys) { - this.nestKeys = nestKeys; - } - - public KeyPathConverter(@NotNull Project project) { - this(ProjectSettingsService.get(project).getState().isNestedKeys()); - } - - public @NotNull String concat(@NotNull KeyPath path) { - StringBuilder builder = new StringBuilder(); - - for(String section : path) { - if(builder.length() > 0) { - if(!this.nestKeys) { - builder.append("\\\\"); - } - - builder.append(KeyPath.DELIMITER); - } - - if(this.nestKeys) { - builder.append(section.replace(KeyPath.DELIMITER, "\\\\" + KeyPath.DELIMITER)); - } else { - builder.append(section); - } - } - - return builder.toString(); - } - - public @NotNull KeyPath split(@NotNull String concatPath) { - String[] sections = concatPath.split(this.nestKeys ? - "(? { - - public KeyPath() {} - - public KeyPath(@Nullable String... path) { - super.addAll(List.of(path)); - } - - public KeyPath(@NotNull List path) { - super(path); - } - - public KeyPath(@NotNull KeyPath path, @Nullable String... pathToAppend) { - this(path); - super.addAll(List.of(pathToAppend)); - } - - @Override - public String toString() { - // Just a simple array view (e.g. [first, second]) - use KeyPathConverter to properly convert a key path - return super.toString(); - } -} diff --git a/src/main/java/de/marhali/easyi18n/model/translation/Translation.java b/src/main/java/de/marhali/easyi18n/model/translation/Translation.java deleted file mode 100644 index 5636c86..0000000 --- a/src/main/java/de/marhali/easyi18n/model/translation/Translation.java +++ /dev/null @@ -1,54 +0,0 @@ -package de.marhali.easyi18n.model.translation; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * Represents a translation with defined key and locale values. - * - * @author marhali - */ -public class Translation { - - private final @NotNull KeyPath key; - private @Nullable TranslationValue value; - - /** - * Constructs a new translation instance. - * @param key Absolute key path - * @param value Values to set - nullable to indicate removal - */ - public Translation(@NotNull KeyPath key, @Nullable TranslationValue value) { - this.key = key; - this.value = value; - } - - /** - * @return Absolute key path - */ - public @NotNull KeyPath getKey() { - return key; - } - - /** - * @return values - nullable to indicate removal - */ - public @Nullable TranslationValue getValue() { - return value; - } - - /** - * @param value Values to set - nullable to indicate removal - */ - public void setValue(@Nullable TranslationValue value) { - this.value = value; - } - - @Override - public String toString() { - return "Translation{" + - "key=" + key + - ", value=" + value + - '}'; - } -} \ No newline at end of file From 92d6e6604dd40581328edf6973da178f8e5836d7 Mon Sep 17 00:00:00 2001 From: marhali Date: Mon, 11 Apr 2022 19:22:58 +0200 Subject: [PATCH 29/51] apply new internal data structure --- .../java/de/marhali/easyi18n/DataBus.java | 2 +- .../de/marhali/easyi18n/InstanceManager.java | 4 +- .../de/marhali/easyi18n/action/AddAction.java | 2 +- .../de/marhali/easyi18n/dialog/AddDialog.java | 8 +- .../marhali/easyi18n/dialog/EditDialog.java | 6 +- .../easyi18n/dialog/TranslationDialog.java | 6 +- .../de/marhali/easyi18n/model/KeyPath.java | 54 ++-- .../marhali/easyi18n/model/Translation.java | 52 +++- .../easyi18n/model/TranslationData.java | 4 +- .../easyi18n/model/TranslationNode.java | 10 +- .../{translation => }/TranslationValue.java | 241 ++++++++++-------- .../model/{ => action}/TranslationCreate.java | 6 +- .../model/{ => action}/TranslationDelete.java | 6 +- .../model/{ => action}/TranslationUpdate.java | 13 +- .../easyi18n/util/KeyPathConverter.java | 15 +- .../easyi18n/KeyPathConverterTest.java | 2 +- .../marhali/easyi18n/TranslationDataTest.java | 200 +++++++-------- 17 files changed, 338 insertions(+), 293 deletions(-) rename src/main/java/de/marhali/easyi18n/model/{translation => }/TranslationValue.java (79%) rename src/main/java/de/marhali/easyi18n/model/{ => action}/TranslationCreate.java (59%) rename src/main/java/de/marhali/easyi18n/model/{ => action}/TranslationDelete.java (60%) rename src/main/java/de/marhali/easyi18n/model/{ => action}/TranslationUpdate.java (69%) diff --git a/src/main/java/de/marhali/easyi18n/DataBus.java b/src/main/java/de/marhali/easyi18n/DataBus.java index d500232..2eb49d0 100644 --- a/src/main/java/de/marhali/easyi18n/DataBus.java +++ b/src/main/java/de/marhali/easyi18n/DataBus.java @@ -1,9 +1,9 @@ package de.marhali.easyi18n; -import de.marhali.easyi18n.model.KeyPath; import de.marhali.easyi18n.model.bus.BusListener; import de.marhali.easyi18n.model.TranslationData; +import de.marhali.easyi18n.model.KeyPath; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/de/marhali/easyi18n/InstanceManager.java b/src/main/java/de/marhali/easyi18n/InstanceManager.java index 1724260..5fe9c55 100644 --- a/src/main/java/de/marhali/easyi18n/InstanceManager.java +++ b/src/main/java/de/marhali/easyi18n/InstanceManager.java @@ -3,7 +3,7 @@ package de.marhali.easyi18n; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; -import de.marhali.easyi18n.model.TranslationUpdate; +import de.marhali.easyi18n.model.action.TranslationUpdate; import org.jetbrains.annotations.NotNull; @@ -67,7 +67,7 @@ public class InstanceManager { } if(!update.isDeletion()) { // Create or re-create translation with changed data - this.store.getData().setTranslation(update.getChange().getKey(), update.getChange().getTranslation()); + this.store.getData().setTranslation(update.getChange().getKey(), update.getChange().getValue()); } this.store.saveToPersistenceLayer(success -> { diff --git a/src/main/java/de/marhali/easyi18n/action/AddAction.java b/src/main/java/de/marhali/easyi18n/action/AddAction.java index 05be4f1..2505a18 100644 --- a/src/main/java/de/marhali/easyi18n/action/AddAction.java +++ b/src/main/java/de/marhali/easyi18n/action/AddAction.java @@ -7,7 +7,7 @@ import com.intellij.openapi.project.Project; import com.intellij.ui.content.Content; import de.marhali.easyi18n.dialog.AddDialog; -import de.marhali.easyi18n.model.translation.KeyPath; +import de.marhali.easyi18n.model.KeyPath; import de.marhali.easyi18n.service.WindowManager; import de.marhali.easyi18n.util.KeyPathConverter; import de.marhali.easyi18n.util.TreeUtil; diff --git a/src/main/java/de/marhali/easyi18n/dialog/AddDialog.java b/src/main/java/de/marhali/easyi18n/dialog/AddDialog.java index 0998611..be14827 100644 --- a/src/main/java/de/marhali/easyi18n/dialog/AddDialog.java +++ b/src/main/java/de/marhali/easyi18n/dialog/AddDialog.java @@ -5,10 +5,10 @@ import com.intellij.openapi.ui.DialogBuilder; import com.intellij.openapi.ui.DialogWrapper; import de.marhali.easyi18n.InstanceManager; -import de.marhali.easyi18n.model.TranslationCreate; -import de.marhali.easyi18n.model.translation.KeyPath; -import de.marhali.easyi18n.model.translation.Translation; -import de.marhali.easyi18n.model.translation.TranslationValue; +import de.marhali.easyi18n.model.action.TranslationCreate; +import de.marhali.easyi18n.model.KeyPath; +import de.marhali.easyi18n.model.Translation; +import de.marhali.easyi18n.model.TranslationValue; import de.marhali.easyi18n.settings.ProjectSettingsService; import org.jetbrains.annotations.NotNull; diff --git a/src/main/java/de/marhali/easyi18n/dialog/EditDialog.java b/src/main/java/de/marhali/easyi18n/dialog/EditDialog.java index 97cc5f5..f9ffa69 100644 --- a/src/main/java/de/marhali/easyi18n/dialog/EditDialog.java +++ b/src/main/java/de/marhali/easyi18n/dialog/EditDialog.java @@ -6,9 +6,9 @@ import com.intellij.openapi.ui.DialogWrapper; import de.marhali.easyi18n.InstanceManager; import de.marhali.easyi18n.dialog.descriptor.DeleteActionDescriptor; -import de.marhali.easyi18n.model.TranslationDelete; -import de.marhali.easyi18n.model.TranslationUpdate; -import de.marhali.easyi18n.model.translation.Translation; +import de.marhali.easyi18n.model.action.TranslationDelete; +import de.marhali.easyi18n.model.action.TranslationUpdate; +import de.marhali.easyi18n.model.Translation; import org.jetbrains.annotations.NotNull; diff --git a/src/main/java/de/marhali/easyi18n/dialog/TranslationDialog.java b/src/main/java/de/marhali/easyi18n/dialog/TranslationDialog.java index 01cacd7..d60c74f 100644 --- a/src/main/java/de/marhali/easyi18n/dialog/TranslationDialog.java +++ b/src/main/java/de/marhali/easyi18n/dialog/TranslationDialog.java @@ -7,9 +7,9 @@ import com.intellij.ui.components.JBTextField; import com.intellij.util.ui.FormBuilder; import de.marhali.easyi18n.InstanceManager; -import de.marhali.easyi18n.model.translation.KeyPath; -import de.marhali.easyi18n.model.translation.Translation; -import de.marhali.easyi18n.model.translation.TranslationValue; +import de.marhali.easyi18n.model.KeyPath; +import de.marhali.easyi18n.model.Translation; +import de.marhali.easyi18n.model.TranslationValue; import de.marhali.easyi18n.settings.ProjectSettings; import de.marhali.easyi18n.settings.ProjectSettingsService; import de.marhali.easyi18n.util.KeyPathConverter; diff --git a/src/main/java/de/marhali/easyi18n/model/KeyPath.java b/src/main/java/de/marhali/easyi18n/model/KeyPath.java index 17e4d1a..0ad3089 100644 --- a/src/main/java/de/marhali/easyi18n/model/KeyPath.java +++ b/src/main/java/de/marhali/easyi18n/model/KeyPath.java @@ -1,59 +1,37 @@ package de.marhali.easyi18n.model; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; -import java.util.Collection; import java.util.List; -import java.util.regex.Pattern; /** - * Represents a full translation key with all sections. - * Implementations can use single section or variable section length variants. - * The respective layer (io, presentation) is responsible for using the correct mapping mechanism. + * Represents the absolute key path for a desired translation. + * The key could be based one or many sections. + * Classes implementing this structure need to take care on how to layer translations paths. * @author marhali */ -@Deprecated public class KeyPath extends ArrayList { - public static final String DELIMITER = "."; + public KeyPath() {} - public static KeyPath of(@NotNull String... path) { - return new KeyPath(List.of(path)); + public KeyPath(@Nullable String... path) { + super.addAll(List.of(path)); } - public KeyPath() { - super(); + public KeyPath(@NotNull List path) { + super(path); } - public KeyPath(@NotNull KeyPath path, String... pathToAppend) { + public KeyPath(@NotNull KeyPath path, @Nullable String... pathToAppend) { this(path); - this.addAll(List.of(pathToAppend)); + super.addAll(List.of(pathToAppend)); } - public KeyPath(@NotNull Collection c) { - super(c); + @Override + public String toString() { + // Just a simple array view (e.g. [first, second]) - use KeyPathConverter to properly convert a key path + return super.toString(); } - - public KeyPath(@NotNull String simplePath) { - this(List.of(simplePath.split(Pattern.quote(DELIMITER)))); - } - - /** - * Note: Use {@link KeyPathConverter} if you want to keep hierarchy. - * @return simple path representation by adding delimiter between the secton nodes - */ - public String toSimpleString() { - StringBuilder builder = new StringBuilder(); - - for(String section : this) { - if(builder.length() > 0) { - builder.append(DELIMITER); - } - - builder.append(section); - } - - return builder.toString(); - } -} \ No newline at end of file +} diff --git a/src/main/java/de/marhali/easyi18n/model/Translation.java b/src/main/java/de/marhali/easyi18n/model/Translation.java index cced758..cfff6c8 100644 --- a/src/main/java/de/marhali/easyi18n/model/Translation.java +++ b/src/main/java/de/marhali/easyi18n/model/Translation.java @@ -1,30 +1,54 @@ package de.marhali.easyi18n.model; -import java.util.HashMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** - * Represents all translations for an element. The assignment to an element is done in the using class. - * This class contains only the translations for this unspecific element. + * Represents a translation with defined key and locale values. + * * @author marhali */ -@Deprecated // Replaced by TranslationValue -public class Translation extends HashMap { - public Translation() { - super(); +public class Translation { + + private final @NotNull KeyPath key; + private @Nullable TranslationValue value; + + /** + * Constructs a new translation instance. + * @param key Absolute key path + * @param value Values to set - nullable to indicate removal + */ + public Translation(@NotNull KeyPath key, @Nullable TranslationValue value) { + this.key = key; + this.value = value; } - public Translation(String locale, String content) { - this(); - super.put(locale, content); + /** + * @return Absolute key path + */ + public @NotNull KeyPath getKey() { + return key; } - public Translation add(String locale, String content) { - super.put(locale, content); - return this; + /** + * @return values - nullable to indicate removal + */ + public @Nullable TranslationValue getValue() { + return value; + } + + /** + * @param value Values to set - nullable to indicate removal + */ + public void setValue(@Nullable TranslationValue value) { + this.value = value; } @Override public String toString() { - return super.toString(); + return "Translation{" + + "key=" + key + + ", value=" + value + + '}'; } } \ No newline at end of file diff --git a/src/main/java/de/marhali/easyi18n/model/TranslationData.java b/src/main/java/de/marhali/easyi18n/model/TranslationData.java index 087048b..33a2946 100644 --- a/src/main/java/de/marhali/easyi18n/model/TranslationData.java +++ b/src/main/java/de/marhali/easyi18n/model/TranslationData.java @@ -93,7 +93,7 @@ public class TranslationData { * @param fullPath Absolute translation key path * @return Found translation. Can be null if path is empty or is not a leaf element */ - public @Nullable Translation getTranslation(@NotNull KeyPath fullPath) { + public @Nullable TranslationValue getTranslation(@NotNull KeyPath fullPath) { TranslationNode node = this.getNode(fullPath); if(node == null || !node.isLeaf()) { @@ -109,7 +109,7 @@ public class TranslationData { * @param fullPath Absolute translation key path * @param translation Translation to set. Can be null to delete the corresponding node */ - public void setTranslation(@NotNull KeyPath fullPath, @Nullable Translation translation) { + public void setTranslation(@NotNull KeyPath fullPath, @Nullable TranslationValue translation) { if(fullPath.isEmpty()) { throw new IllegalArgumentException("Key path cannot be empty"); } diff --git a/src/main/java/de/marhali/easyi18n/model/TranslationNode.java b/src/main/java/de/marhali/easyi18n/model/TranslationNode.java index d85e678..b2ca570 100644 --- a/src/main/java/de/marhali/easyi18n/model/TranslationNode.java +++ b/src/main/java/de/marhali/easyi18n/model/TranslationNode.java @@ -27,7 +27,7 @@ public class TranslationNode { private Map children; @NotNull - private Translation value; + private TranslationValue value; public TranslationNode(boolean sort) { this(sort ? new TreeMap<>() : new LinkedHashMap<>()); @@ -40,7 +40,7 @@ public class TranslationNode { public TranslationNode(@NotNull Map children) { this.parent = null; this.children = children; - this.value = new Translation(); + this.value = new TranslationValue(); } /** @@ -62,11 +62,11 @@ public class TranslationNode { this.parent = parent; } - public @NotNull Translation getValue() { + public @NotNull TranslationValue getValue() { return value; } - public void setValue(@NotNull Translation value) { + public void setValue(@NotNull TranslationValue value) { this.children.clear(); this.value = value; } @@ -93,7 +93,7 @@ public class TranslationNode { } } - public void setChildren(@NotNull String key, @NotNull Translation translation) { + public void setChildren(@NotNull String key, @NotNull TranslationValue translation) { this.setChildren(key).setValue(translation); } diff --git a/src/main/java/de/marhali/easyi18n/model/translation/TranslationValue.java b/src/main/java/de/marhali/easyi18n/model/TranslationValue.java similarity index 79% rename from src/main/java/de/marhali/easyi18n/model/translation/TranslationValue.java rename to src/main/java/de/marhali/easyi18n/model/TranslationValue.java index f3e2688..6abd17b 100644 --- a/src/main/java/de/marhali/easyi18n/model/translation/TranslationValue.java +++ b/src/main/java/de/marhali/easyi18n/model/TranslationValue.java @@ -1,107 +1,134 @@ -package de.marhali.easyi18n.model.translation; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.HashMap; -import java.util.Map; - -/** - * Represents the set values behind a specific translation. - * @author marhali - */ -public class TranslationValue { - - private @Nullable String description; - private @NotNull Map values; - private @Nullable Object misc; - - public TranslationValue(@Nullable String description, @NotNull Map values, @Nullable Object misc) { - this.description = description; - this.values = values; - this.misc = misc; - } - - public TranslationValue(@NotNull Map values) { - this(null, values, null); - } - - public TranslationValue(@NotNull String locale, @NotNull String value) { - this(Map.of(locale, value)); - } - - public TranslationValue() { - this(null, new HashMap<>(), null); - } - - /** - * Retrieve additional description for this translation - * @return Description - */ - public @Nullable String getDescription() { - return description; - } - - /** - * Override or set description for this translation - * @param description Description - */ - public void setDescription(@Nullable String description) { - this.description = description; - } - - /** - * Set locale specific values. - * @param values New values - */ - public void setValues(@NotNull Map values) { - this.values = values; - } - - /** - * Overrides or sets a value for a specific locale. - * @param locale Locale type - * @param value New value - */ - public void put(@NotNull String locale, @Nullable String value) { - if(value == null) { // Delete operation - values.remove(locale); - } else { - values.put(locale, value); - } - } - - /** - * Retrieves the associated value for a specific locale - * @param locale Locale type - * @return Value or null if missing - */ - public @Nullable String get(@NotNull String locale) { - return values.get(locale); - } - - /** - * I18n support data - * @return Data - */ - public @Nullable Object getMisc() { - return misc; - } - - /** - * Set or update I18n support data - * @param misc New Data - */ - public void setMisc(@Nullable Object misc) { - this.misc = misc; - } - - @Override - public String toString() { - return "TranslationValue{" + - "description='" + description + '\'' + - ", values=" + values + - ", misc=" + misc + - '}'; - } -} +package de.marhali.easyi18n.model; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +/** + * Represents the set values behind a specific translation. + * @author marhali + */ +public class TranslationValue { + + private @Nullable String description; + private @NotNull Map values; + private @Nullable Object misc; + + public TranslationValue(@Nullable String description, @NotNull Map values, @Nullable Object misc) { + this.description = description; + this.values = values; + this.misc = misc; + } + + public TranslationValue(@NotNull Map values) { + this(null, values, null); + } + + public TranslationValue(@NotNull String locale, @NotNull String value) { + this(new HashMap<>(Map.of(locale, value))); + } + + public TranslationValue() { + this(null, new HashMap<>(), null); + } + + /** + * Retrieve additional description for this translation + * @return Description + */ + public @Nullable String getDescription() { + return description; + } + + /** + * Override or set description for this translation + * @param description Description + */ + public void setDescription(@Nullable String description) { + this.description = description; + } + + /** + * Set locale specific values. + * @param values New values + */ + public void setValues(@NotNull Map values) { + this.values = values; + } + + public @NotNull Map getValues() { + return values; + } + + /** + * Overrides or sets a value for a specific locale. + * @param locale Locale type + * @param value New value + */ + public void put(@NotNull String locale, @Nullable String value) { + if(value == null) { // Delete operation + values.remove(locale); + } else { + values.put(locale, value); + } + } + + public void remove(String locale) { + values.remove(locale); + } + + /** + * Retrieves the associated value for a specific locale + * @param locale Locale type + * @return Value or null if missing + */ + public @Nullable String get(@NotNull String locale) { + return values.get(locale); + } + + public boolean containsLocale(@Nullable String locale) { + return values.containsKey(locale); + } + + public @NotNull Collection getLocaleValues() { + return values.values(); + } + + public int size() { + return values.size(); + } + + public void clear() { + description = null; + values.clear(); + misc = null; + } + + /** + * I18n support data + * @return Data + */ + public @Nullable Object getMisc() { + return misc; + } + + /** + * Set or update I18n support data + * @param misc New Data + */ + public void setMisc(@Nullable Object misc) { + this.misc = misc; + } + + @Override + public String toString() { + return "TranslationValue{" + + "description='" + description + '\'' + + ", values=" + values + + ", misc=" + misc + + '}'; + } +} diff --git a/src/main/java/de/marhali/easyi18n/model/TranslationCreate.java b/src/main/java/de/marhali/easyi18n/model/action/TranslationCreate.java similarity index 59% rename from src/main/java/de/marhali/easyi18n/model/TranslationCreate.java rename to src/main/java/de/marhali/easyi18n/model/action/TranslationCreate.java index 9c955a9..4edd8f5 100644 --- a/src/main/java/de/marhali/easyi18n/model/TranslationCreate.java +++ b/src/main/java/de/marhali/easyi18n/model/action/TranslationCreate.java @@ -1,4 +1,6 @@ -package de.marhali.easyi18n.model; +package de.marhali.easyi18n.model.action; + +import de.marhali.easyi18n.model.Translation; import org.jetbrains.annotations.NotNull; @@ -7,7 +9,7 @@ import org.jetbrains.annotations.NotNull; * @author marhali */ public class TranslationCreate extends TranslationUpdate { - public TranslationCreate(@NotNull KeyedTranslation translation) { + public TranslationCreate(@NotNull Translation translation) { super(null, translation); } } \ No newline at end of file diff --git a/src/main/java/de/marhali/easyi18n/model/TranslationDelete.java b/src/main/java/de/marhali/easyi18n/model/action/TranslationDelete.java similarity index 60% rename from src/main/java/de/marhali/easyi18n/model/TranslationDelete.java rename to src/main/java/de/marhali/easyi18n/model/action/TranslationDelete.java index 763cb16..6812e86 100644 --- a/src/main/java/de/marhali/easyi18n/model/TranslationDelete.java +++ b/src/main/java/de/marhali/easyi18n/model/action/TranslationDelete.java @@ -1,4 +1,6 @@ -package de.marhali.easyi18n.model; +package de.marhali.easyi18n.model.action; + +import de.marhali.easyi18n.model.Translation; import org.jetbrains.annotations.NotNull; @@ -7,7 +9,7 @@ import org.jetbrains.annotations.NotNull; * @author marhali */ public class TranslationDelete extends TranslationUpdate { - public TranslationDelete(@NotNull KeyedTranslation translation) { + public TranslationDelete(@NotNull Translation translation) { super(translation, null); } } \ No newline at end of file diff --git a/src/main/java/de/marhali/easyi18n/model/TranslationUpdate.java b/src/main/java/de/marhali/easyi18n/model/action/TranslationUpdate.java similarity index 69% rename from src/main/java/de/marhali/easyi18n/model/TranslationUpdate.java rename to src/main/java/de/marhali/easyi18n/model/action/TranslationUpdate.java index 6b9dac8..b1a03b3 100644 --- a/src/main/java/de/marhali/easyi18n/model/TranslationUpdate.java +++ b/src/main/java/de/marhali/easyi18n/model/action/TranslationUpdate.java @@ -1,5 +1,6 @@ -package de.marhali.easyi18n.model; +package de.marhali.easyi18n.model.action; +import de.marhali.easyi18n.model.Translation; import org.jetbrains.annotations.Nullable; /** @@ -10,19 +11,19 @@ import org.jetbrains.annotations.Nullable; */ public class TranslationUpdate { - private final @Nullable KeyedTranslation origin; - private final @Nullable KeyedTranslation change; + private final @Nullable Translation origin; + private final @Nullable Translation change; - public TranslationUpdate(@Nullable KeyedTranslation origin, @Nullable KeyedTranslation change) { + public TranslationUpdate(@Nullable Translation origin, @Nullable Translation change) { this.origin = origin; this.change = change; } - public @Nullable KeyedTranslation getOrigin() { + public @Nullable Translation getOrigin() { return origin; } - public @Nullable KeyedTranslation getChange() { + public @Nullable Translation getChange() { return change; } diff --git a/src/main/java/de/marhali/easyi18n/util/KeyPathConverter.java b/src/main/java/de/marhali/easyi18n/util/KeyPathConverter.java index cde4cd2..3307d6c 100644 --- a/src/main/java/de/marhali/easyi18n/util/KeyPathConverter.java +++ b/src/main/java/de/marhali/easyi18n/util/KeyPathConverter.java @@ -1,7 +1,10 @@ package de.marhali.easyi18n.util; -import de.marhali.easyi18n.model.translation.KeyPath; +import com.intellij.openapi.project.Project; + +import de.marhali.easyi18n.model.KeyPath; import de.marhali.easyi18n.settings.ProjectSettings; +import de.marhali.easyi18n.settings.ProjectSettingsService; import org.jetbrains.annotations.NotNull; @@ -20,10 +23,18 @@ public class KeyPathConverter { * Constructs a new converter instance * @param settings Delimiter configuration */ - public KeyPathConverter(ProjectSettings settings) { + public KeyPathConverter(@NotNull ProjectSettings settings) { this.settings = settings; } + /** + * @see #KeyPathConverter(ProjectSettings) + * @param project Opened project + */ + public KeyPathConverter(@NotNull Project project) { + this(ProjectSettingsService.get(project).getState()); + } + /** * Transform to character literal representation * @param path Absolute key path diff --git a/src/test/java/de/marhali/easyi18n/KeyPathConverterTest.java b/src/test/java/de/marhali/easyi18n/KeyPathConverterTest.java index 9718215..8404ba1 100644 --- a/src/test/java/de/marhali/easyi18n/KeyPathConverterTest.java +++ b/src/test/java/de/marhali/easyi18n/KeyPathConverterTest.java @@ -2,7 +2,7 @@ package de.marhali.easyi18n; import de.marhali.easyi18n.io.parser.ParserStrategyType; import de.marhali.easyi18n.model.FolderStrategyType; -import de.marhali.easyi18n.model.translation.KeyPath; +import de.marhali.easyi18n.model.KeyPath; import de.marhali.easyi18n.settings.ProjectSettings; import de.marhali.easyi18n.util.KeyPathConverter; diff --git a/src/test/java/de/marhali/easyi18n/TranslationDataTest.java b/src/test/java/de/marhali/easyi18n/TranslationDataTest.java index 4e788f0..24f4c0f 100644 --- a/src/test/java/de/marhali/easyi18n/TranslationDataTest.java +++ b/src/test/java/de/marhali/easyi18n/TranslationDataTest.java @@ -1,10 +1,10 @@ package de.marhali.easyi18n; -import de.marhali.easyi18n.model.KeyPath; -import de.marhali.easyi18n.model.Translation; import de.marhali.easyi18n.model.TranslationData; import de.marhali.easyi18n.model.TranslationNode; +import de.marhali.easyi18n.model.KeyPath; +import de.marhali.easyi18n.model.TranslationValue; import org.junit.Assert; import org.junit.Test; @@ -17,27 +17,27 @@ import java.util.*; public class TranslationDataTest { private final int numOfTranslations = 14; - private final Translation translation = new Translation("en", "test"); + private final TranslationValue translation = new TranslationValue("en", "test"); private void addTranslations(TranslationData data) { - data.setTranslation(KeyPath.of("zulu"), translation); - data.setTranslation(KeyPath.of("gamma"), translation); + data.setTranslation(new KeyPath("zulu"), translation); + data.setTranslation(new KeyPath("gamma"), translation); - data.setTranslation(KeyPath.of("foxtrot.super.long.key"), translation); - data.setTranslation(KeyPath.of("foxtrot", "super", "long", "key"), translation); + data.setTranslation(new KeyPath("foxtrot.super.long.key"), translation); + data.setTranslation(new KeyPath("foxtrot", "super", "long", "key"), translation); - data.setTranslation(KeyPath.of("charlie.b", "sub"), translation); - data.setTranslation(KeyPath.of("charlie.a", "sub"), translation); + data.setTranslation(new KeyPath("charlie.b", "sub"), translation); + data.setTranslation(new KeyPath("charlie.a", "sub"), translation); - data.setTranslation(KeyPath.of("bravo.b"), translation); - data.setTranslation(KeyPath.of("bravo.c"), translation); - data.setTranslation(KeyPath.of("bravo.a"), translation); - data.setTranslation(KeyPath.of("bravo.d"), translation); + data.setTranslation(new KeyPath("bravo.b"), translation); + data.setTranslation(new KeyPath("bravo.c"), translation); + data.setTranslation(new KeyPath("bravo.a"), translation); + data.setTranslation(new KeyPath("bravo.d"), translation); - data.setTranslation(KeyPath.of("bravo", "b"), translation); - data.setTranslation(KeyPath.of("bravo", "c"), translation); - data.setTranslation(KeyPath.of("bravo", "a"), translation); - data.setTranslation(KeyPath.of("bravo", "d"), translation); + data.setTranslation(new KeyPath("bravo", "b"), translation); + data.setTranslation(new KeyPath("bravo", "c"), translation); + data.setTranslation(new KeyPath("bravo", "a"), translation); + data.setTranslation(new KeyPath("bravo", "d"), translation); } @Test @@ -46,13 +46,13 @@ public class TranslationDataTest { this.addTranslations(data); Set expectation = new LinkedHashSet<>(Arrays.asList( - KeyPath.of("bravo", "a"), KeyPath.of("bravo", "b"), KeyPath.of("bravo", "c"), KeyPath.of("bravo", "d"), - KeyPath.of("bravo.a"), KeyPath.of("bravo.b"), KeyPath.of("bravo.c"), KeyPath.of("bravo.d"), - KeyPath.of("charlie.a", "sub"), KeyPath.of("charlie.b", "sub"), - KeyPath.of("foxtrot", "super", "long", "key"), - KeyPath.of("foxtrot.super.long.key"), - KeyPath.of("gamma"), - KeyPath.of("zulu") + new KeyPath("bravo", "a"), new KeyPath("bravo", "b"), new KeyPath("bravo", "c"), new KeyPath("bravo", "d"), + new KeyPath("bravo.a"), new KeyPath("bravo.b"), new KeyPath("bravo.c"), new KeyPath("bravo.d"), + new KeyPath("charlie.a", "sub"), new KeyPath("charlie.b", "sub"), + new KeyPath("foxtrot", "super", "long", "key"), + new KeyPath("foxtrot.super.long.key"), + new KeyPath("gamma"), + new KeyPath("zulu") )); Assert.assertEquals(data.getFullKeys(), expectation); @@ -65,13 +65,13 @@ public class TranslationDataTest { this.addTranslations(data); Set expectation = new LinkedHashSet<>(Arrays.asList( - KeyPath.of("zulu"), - KeyPath.of("gamma"), - KeyPath.of("foxtrot.super.long.key"), - KeyPath.of("foxtrot", "super", "long", "key"), - KeyPath.of("charlie.b", "sub"), KeyPath.of("charlie.a", "sub"), - KeyPath.of("bravo.b"), KeyPath.of("bravo.c"), KeyPath.of("bravo.a"), KeyPath.of("bravo.d"), - KeyPath.of("bravo", "b"), KeyPath.of("bravo", "c"), KeyPath.of("bravo", "a"), KeyPath.of("bravo", "d") + new KeyPath("zulu"), + new KeyPath("gamma"), + new KeyPath("foxtrot.super.long.key"), + new KeyPath("foxtrot", "super", "long", "key"), + new KeyPath("charlie.b", "sub"), new KeyPath("charlie.a", "sub"), + new KeyPath("bravo.b"), new KeyPath("bravo.c"), new KeyPath("bravo.a"), new KeyPath("bravo.d"), + new KeyPath("bravo", "b"), new KeyPath("bravo", "c"), new KeyPath("bravo", "a"), new KeyPath("bravo", "d") )); Assert.assertEquals(data.getFullKeys(), expectation); @@ -82,35 +82,35 @@ public class TranslationDataTest { public void testDelete() { TranslationData data = new TranslationData(true); - data.setTranslation(KeyPath.of("alpha"), translation); - data.setTranslation(KeyPath.of("nested.alpha"), translation); - data.setTranslation(KeyPath.of("nested.long.bravo"), translation); + data.setTranslation(new KeyPath("alpha"), translation); + data.setTranslation(new KeyPath("nested.alpha"), translation); + data.setTranslation(new KeyPath("nested.long.bravo"), translation); - data.setTranslation(KeyPath.of("beta"), translation); - data.setTranslation(KeyPath.of("nested", "alpha"), translation); - data.setTranslation(KeyPath.of("nested", "long", "bravo"), translation); + data.setTranslation(new KeyPath("beta"), translation); + data.setTranslation(new KeyPath("nested", "alpha"), translation); + data.setTranslation(new KeyPath("nested", "long", "bravo"), translation); Assert.assertEquals(data.getFullKeys().size(), 6); - data.setTranslation(KeyPath.of("alpha"), null); - data.setTranslation(KeyPath.of("nested.alpha"), null); - data.setTranslation(KeyPath.of("nested.long.bravo"), null); + data.setTranslation(new KeyPath("alpha"), null); + data.setTranslation(new KeyPath("nested.alpha"), null); + data.setTranslation(new KeyPath("nested.long.bravo"), null); Assert.assertEquals(data.getFullKeys().size(), 3); - data.setTranslation(KeyPath.of("beta"), null); - data.setTranslation(KeyPath.of("nested", "alpha"), null); - data.setTranslation(KeyPath.of("nested", "long", "bravo"), null); + data.setTranslation(new KeyPath("beta"), null); + data.setTranslation(new KeyPath("nested", "alpha"), null); + data.setTranslation(new KeyPath("nested", "long", "bravo"), null); Assert.assertEquals(data.getFullKeys().size(), 0); - Assert.assertNull(data.getTranslation(KeyPath.of("alpha"))); - Assert.assertNull(data.getTranslation(KeyPath.of("nested.alpha"))); - Assert.assertNull(data.getTranslation(KeyPath.of("nested.long.bravo"))); + Assert.assertNull(data.getTranslation(new KeyPath("alpha"))); + Assert.assertNull(data.getTranslation(new KeyPath("nested.alpha"))); + Assert.assertNull(data.getTranslation(new KeyPath("nested.long.bravo"))); - Assert.assertNull(data.getTranslation(KeyPath.of("beta"))); - Assert.assertNull(data.getTranslation(KeyPath.of("nested", "alpha"))); - Assert.assertNull(data.getTranslation(KeyPath.of("nested", "long", "bravo"))); + Assert.assertNull(data.getTranslation(new KeyPath("beta"))); + Assert.assertNull(data.getTranslation(new KeyPath("nested", "alpha"))); + Assert.assertNull(data.getTranslation(new KeyPath("nested", "long", "bravo"))); } @Test @@ -118,10 +118,10 @@ public class TranslationDataTest { TranslationData data = new TranslationData(true); this.addTranslations(data); - data.setTranslation(KeyPath.of("foxtrot.super.long.key"), null); - data.setTranslation(KeyPath.of("foxtrot", "super", "long", "key"), null); + data.setTranslation(new KeyPath("foxtrot.super.long.key"), null); + data.setTranslation(new KeyPath("foxtrot", "super", "long", "key"), null); - Assert.assertNull(data.getTranslation(KeyPath.of("foxtrot.super.long.key"))); + Assert.assertNull(data.getTranslation(new KeyPath("foxtrot.super.long.key"))); Assert.assertNull(data.getRootNode().getChildren().get("foxtrot")); Assert.assertEquals(data.getFullKeys().size(), numOfTranslations - 2); } @@ -130,70 +130,70 @@ public class TranslationDataTest { public void testOverwrite() { TranslationData data = new TranslationData(true); - Translation before = new Translation("en", "before"); - Translation after = new Translation("en", "after"); + TranslationValue before = new TranslationValue("en", "before"); + TranslationValue after = new TranslationValue("en", "after"); - data.setTranslation(KeyPath.of("alpha"), before); - data.setTranslation(KeyPath.of("nested.alpha"), before); - data.setTranslation(KeyPath.of("nested.long.bravo"), before); - data.setTranslation(KeyPath.of("beta"), before); - data.setTranslation(KeyPath.of("nested", "alpha"), before); - data.setTranslation(KeyPath.of("nested", "long", "bravo"), before); + data.setTranslation(new KeyPath("alpha"), before); + data.setTranslation(new KeyPath("nested.alpha"), before); + data.setTranslation(new KeyPath("nested.long.bravo"), before); + data.setTranslation(new KeyPath("beta"), before); + data.setTranslation(new KeyPath("nested", "alpha"), before); + data.setTranslation(new KeyPath("nested", "long", "bravo"), before); - Assert.assertEquals(data.getTranslation(KeyPath.of("alpha")), before); - Assert.assertEquals(data.getTranslation(KeyPath.of("nested.alpha")), before); - Assert.assertEquals(data.getTranslation(KeyPath.of("nested.long.bravo")), before); - Assert.assertEquals(data.getTranslation(KeyPath.of("beta")), before); - Assert.assertEquals(data.getTranslation(KeyPath.of("nested", "alpha")), before); - Assert.assertEquals(data.getTranslation(KeyPath.of("nested", "long", "bravo")), before); + Assert.assertEquals(data.getTranslation(new KeyPath("alpha")), before); + Assert.assertEquals(data.getTranslation(new KeyPath("nested.alpha")), before); + Assert.assertEquals(data.getTranslation(new KeyPath("nested.long.bravo")), before); + Assert.assertEquals(data.getTranslation(new KeyPath("beta")), before); + Assert.assertEquals(data.getTranslation(new KeyPath("nested", "alpha")), before); + Assert.assertEquals(data.getTranslation(new KeyPath("nested", "long", "bravo")), before); - data.setTranslation(KeyPath.of("alpha"), after); - data.setTranslation(KeyPath.of("nested.alpha"), after); - data.setTranslation(KeyPath.of("nested.long.bravo"), after); - data.setTranslation(KeyPath.of("beta"), after); - data.setTranslation(KeyPath.of("nested", "alpha"), after); - data.setTranslation(KeyPath.of("nested", "long", "bravo"), after); + data.setTranslation(new KeyPath("alpha"), after); + data.setTranslation(new KeyPath("nested.alpha"), after); + data.setTranslation(new KeyPath("nested.long.bravo"), after); + data.setTranslation(new KeyPath("beta"), after); + data.setTranslation(new KeyPath("nested", "alpha"), after); + data.setTranslation(new KeyPath("nested", "long", "bravo"), after); - Assert.assertEquals(data.getTranslation(KeyPath.of("alpha")), after); - Assert.assertEquals(data.getTranslation(KeyPath.of("nested.alpha")), after); - Assert.assertEquals(data.getTranslation(KeyPath.of("nested.long.bravo")), after); - Assert.assertEquals(data.getTranslation(KeyPath.of("beta")), after); - Assert.assertEquals(data.getTranslation(KeyPath.of("nested", "alpha")), after); - Assert.assertEquals(data.getTranslation(KeyPath.of("nested", "long", "bravo")), after); + Assert.assertEquals(data.getTranslation(new KeyPath("alpha")), after); + Assert.assertEquals(data.getTranslation(new KeyPath("nested.alpha")), after); + Assert.assertEquals(data.getTranslation(new KeyPath("nested.long.bravo")), after); + Assert.assertEquals(data.getTranslation(new KeyPath("beta")), after); + Assert.assertEquals(data.getTranslation(new KeyPath("nested", "alpha")), after); + Assert.assertEquals(data.getTranslation(new KeyPath("nested", "long", "bravo")), after); } @Test public void testTransformRecursively() { TranslationData data = new TranslationData(true); - data.setTranslation(KeyPath.of("alpha.nested.key"), translation); - data.setTranslation(KeyPath.of("alpha.other"), translation); - data.setTranslation(KeyPath.of("bravo"), translation); - data.setTranslation(KeyPath.of("alpha", "nested", "key"), translation); - data.setTranslation(KeyPath.of("alpha", "other"), translation); - data.setTranslation(KeyPath.of("charlie"), translation); + data.setTranslation(new KeyPath("alpha.nested.key"), translation); + data.setTranslation(new KeyPath("alpha.other"), translation); + data.setTranslation(new KeyPath("bravo"), translation); + data.setTranslation(new KeyPath("alpha", "nested", "key"), translation); + data.setTranslation(new KeyPath("alpha", "other"), translation); + data.setTranslation(new KeyPath("charlie"), translation); Assert.assertEquals(6, data.getFullKeys().size()); - data.setTranslation(KeyPath.of("alpha.nested"), translation); - data.setTranslation(KeyPath.of("alpha.other.new"), translation); - data.setTranslation(KeyPath.of("bravo"), null); - data.setTranslation(KeyPath.of("alpha", "nested"), translation); - data.setTranslation(KeyPath.of("alpha", "other", "new"), translation); - data.setTranslation(KeyPath.of("charlie"), null); + data.setTranslation(new KeyPath("alpha.nested"), translation); + data.setTranslation(new KeyPath("alpha.other.new"), translation); + data.setTranslation(new KeyPath("bravo"), null); + data.setTranslation(new KeyPath("alpha", "nested"), translation); + data.setTranslation(new KeyPath("alpha", "other", "new"), translation); + data.setTranslation(new KeyPath("charlie"), null); Assert.assertEquals(6, data.getFullKeys().size()); - Assert.assertNotNull(data.getTranslation(KeyPath.of("alpha.nested.key"))); - Assert.assertNotNull(data.getTranslation(KeyPath.of("alpha.other"))); - Assert.assertNull(data.getTranslation(KeyPath.of("bravo"))); - Assert.assertEquals(data.getTranslation(KeyPath.of("alpha.nested")), translation); - Assert.assertEquals(data.getTranslation(KeyPath.of("alpha.other.new")), translation); + Assert.assertNotNull(data.getTranslation(new KeyPath("alpha.nested.key"))); + Assert.assertNotNull(data.getTranslation(new KeyPath("alpha.other"))); + Assert.assertNull(data.getTranslation(new KeyPath("bravo"))); + Assert.assertEquals(data.getTranslation(new KeyPath("alpha.nested")), translation); + Assert.assertEquals(data.getTranslation(new KeyPath("alpha.other.new")), translation); - Assert.assertNull(data.getTranslation(KeyPath.of("alpha", "nested", "key"))); - Assert.assertNull(data.getTranslation(KeyPath.of("alpha", "other"))); - Assert.assertNull(data.getTranslation(KeyPath.of("charlie"))); - Assert.assertEquals(data.getTranslation(KeyPath.of("alpha", "nested")), translation); - Assert.assertEquals(data.getTranslation(KeyPath.of("alpha", "other", "new")), translation); + Assert.assertNull(data.getTranslation(new KeyPath("alpha", "nested", "key"))); + Assert.assertNull(data.getTranslation(new KeyPath("alpha", "other"))); + Assert.assertNull(data.getTranslation(new KeyPath("charlie"))); + Assert.assertEquals(data.getTranslation(new KeyPath("alpha", "nested")), translation); + Assert.assertEquals(data.getTranslation(new KeyPath("alpha", "other", "new")), translation); } } \ No newline at end of file From 06f03ea1e1cd2f691bc8b95fee85fe07c50bb7da Mon Sep 17 00:00:00 2001 From: marhali Date: Mon, 11 Apr 2022 19:27:36 +0200 Subject: [PATCH 30/51] move FolderStrategyType to right package --- .../easyi18n/{model => io/folder}/FolderStrategyType.java | 7 +------ .../java/de/marhali/easyi18n/io/parser/ParserStrategy.java | 5 +++-- .../java/de/marhali/easyi18n/settings/ProjectSettings.java | 2 +- .../easyi18n/settings/ProjectSettingsComponentState.java | 2 +- .../de/marhali/easyi18n/settings/ProjectSettingsState.java | 2 +- .../marhali/easyi18n/settings/presets/DefaultPreset.java | 2 +- 6 files changed, 8 insertions(+), 12 deletions(-) rename src/main/java/de/marhali/easyi18n/{model => io/folder}/FolderStrategyType.java (81%) diff --git a/src/main/java/de/marhali/easyi18n/model/FolderStrategyType.java b/src/main/java/de/marhali/easyi18n/io/folder/FolderStrategyType.java similarity index 81% rename from src/main/java/de/marhali/easyi18n/model/FolderStrategyType.java rename to src/main/java/de/marhali/easyi18n/io/folder/FolderStrategyType.java index 212538b..047a563 100644 --- a/src/main/java/de/marhali/easyi18n/model/FolderStrategyType.java +++ b/src/main/java/de/marhali/easyi18n/io/folder/FolderStrategyType.java @@ -1,9 +1,4 @@ -package de.marhali.easyi18n.model; - -import de.marhali.easyi18n.io.folder.FolderStrategy; -import de.marhali.easyi18n.io.folder.ModularLocaleFolderStrategy; -import de.marhali.easyi18n.io.folder.ModularNamespaceFolderStrategy; -import de.marhali.easyi18n.io.folder.SingleFolderStrategy; +package de.marhali.easyi18n.io.folder; /** * Represents all supported folder strategies. diff --git a/src/main/java/de/marhali/easyi18n/io/parser/ParserStrategy.java b/src/main/java/de/marhali/easyi18n/io/parser/ParserStrategy.java index 07bc38c..b94c0b3 100644 --- a/src/main/java/de/marhali/easyi18n/io/parser/ParserStrategy.java +++ b/src/main/java/de/marhali/easyi18n/io/parser/ParserStrategy.java @@ -2,6 +2,7 @@ package de.marhali.easyi18n.io.parser; import de.marhali.easyi18n.model.*; +import de.marhali.easyi18n.model.KeyPath; import de.marhali.easyi18n.settings.ProjectSettings; import org.jetbrains.annotations.NotNull; @@ -46,7 +47,7 @@ public abstract class ParserStrategy { if(file.getNamespace() != null) { String moduleName = file.getNamespace(); - TranslationNode moduleNode = data.getNode(KeyPath.of(moduleName)); + TranslationNode moduleNode = data.getNode(new KeyPath(moduleName)); if(moduleNode == null) { moduleNode = new TranslationNode(this.settings.isSorting()); @@ -69,7 +70,7 @@ public abstract class ParserStrategy { TranslationNode targetNode = data.getRootNode(); if(file.getNamespace() != null) { - targetNode = data.getNode(KeyPath.of(file.getNamespace())); + targetNode = data.getNode(new KeyPath(file.getNamespace())); } return Objects.requireNonNull(targetNode); diff --git a/src/main/java/de/marhali/easyi18n/settings/ProjectSettings.java b/src/main/java/de/marhali/easyi18n/settings/ProjectSettings.java index 1f17de5..5b73e9d 100644 --- a/src/main/java/de/marhali/easyi18n/settings/ProjectSettings.java +++ b/src/main/java/de/marhali/easyi18n/settings/ProjectSettings.java @@ -1,7 +1,7 @@ package de.marhali.easyi18n.settings; import de.marhali.easyi18n.io.parser.ParserStrategyType; -import de.marhali.easyi18n.model.FolderStrategyType; +import de.marhali.easyi18n.io.folder.FolderStrategyType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/de/marhali/easyi18n/settings/ProjectSettingsComponentState.java b/src/main/java/de/marhali/easyi18n/settings/ProjectSettingsComponentState.java index 3091472..f3dd07e 100644 --- a/src/main/java/de/marhali/easyi18n/settings/ProjectSettingsComponentState.java +++ b/src/main/java/de/marhali/easyi18n/settings/ProjectSettingsComponentState.java @@ -4,7 +4,7 @@ import com.intellij.openapi.ui.ComboBox; import com.intellij.openapi.ui.TextFieldWithBrowseButton; import de.marhali.easyi18n.io.parser.ParserStrategyType; -import de.marhali.easyi18n.model.FolderStrategyType; +import de.marhali.easyi18n.io.folder.FolderStrategyType; import de.marhali.easyi18n.settings.presets.Preset; import javax.swing.*; diff --git a/src/main/java/de/marhali/easyi18n/settings/ProjectSettingsState.java b/src/main/java/de/marhali/easyi18n/settings/ProjectSettingsState.java index 3ec02df..d4a077d 100644 --- a/src/main/java/de/marhali/easyi18n/settings/ProjectSettingsState.java +++ b/src/main/java/de/marhali/easyi18n/settings/ProjectSettingsState.java @@ -1,7 +1,7 @@ package de.marhali.easyi18n.settings; import de.marhali.easyi18n.io.parser.ParserStrategyType; -import de.marhali.easyi18n.model.FolderStrategyType; +import de.marhali.easyi18n.io.folder.FolderStrategyType; import de.marhali.easyi18n.settings.presets.DefaultPreset; import org.jetbrains.annotations.NotNull; diff --git a/src/main/java/de/marhali/easyi18n/settings/presets/DefaultPreset.java b/src/main/java/de/marhali/easyi18n/settings/presets/DefaultPreset.java index fb515fe..124f1d9 100644 --- a/src/main/java/de/marhali/easyi18n/settings/presets/DefaultPreset.java +++ b/src/main/java/de/marhali/easyi18n/settings/presets/DefaultPreset.java @@ -1,7 +1,7 @@ package de.marhali.easyi18n.settings.presets; import de.marhali.easyi18n.io.parser.ParserStrategyType; -import de.marhali.easyi18n.model.FolderStrategyType; +import de.marhali.easyi18n.io.folder.FolderStrategyType; import de.marhali.easyi18n.settings.ProjectSettings; import org.jetbrains.annotations.NotNull; From 96fc3ad76d32025f71146637b1cd8a510bb5b422 Mon Sep 17 00:00:00 2001 From: marhali Date: Mon, 11 Apr 2022 22:06:05 +0200 Subject: [PATCH 31/51] move FolderStrategyType to right package --- src/test/java/de/marhali/easyi18n/KeyPathConverterTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/de/marhali/easyi18n/KeyPathConverterTest.java b/src/test/java/de/marhali/easyi18n/KeyPathConverterTest.java index 8404ba1..fc71352 100644 --- a/src/test/java/de/marhali/easyi18n/KeyPathConverterTest.java +++ b/src/test/java/de/marhali/easyi18n/KeyPathConverterTest.java @@ -1,7 +1,7 @@ package de.marhali.easyi18n; import de.marhali.easyi18n.io.parser.ParserStrategyType; -import de.marhali.easyi18n.model.FolderStrategyType; +import de.marhali.easyi18n.io.folder.FolderStrategyType; import de.marhali.easyi18n.model.KeyPath; import de.marhali.easyi18n.settings.ProjectSettings; import de.marhali.easyi18n.util.KeyPathConverter; From 7ad19741b4b8c4f6e46f0a9686c536072ada319f Mon Sep 17 00:00:00 2001 From: marhali Date: Mon, 11 Apr 2022 22:06:21 +0200 Subject: [PATCH 32/51] remove unused translation --- src/main/resources/messages.properties | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties index c3eb368..5d3cc88 100644 --- a/src/main/resources/messages.properties +++ b/src/main/resources/messages.properties @@ -11,7 +11,6 @@ action.settings=Settings action.search=Search... action.delete=Delete translation.key=Key -translation.description=Description translation.locales=Locales # Settings settings.hint.text=Project-specific configuration. For an easy start, you can use one of the existing presets. From c348ea7d7801193455cbb8fd61d80284571f634e Mon Sep 17 00:00:00 2001 From: marhali Date: Mon, 11 Apr 2022 22:07:18 +0200 Subject: [PATCH 33/51] update mapper to new data structure --- .../easyi18n/io/parser/json/JsonMapper.java | 6 +- .../easyi18n/io/parser/json5/Json5Mapper.java | 8 +- .../parser/properties/PropertiesMapper.java | 27 ++-- .../properties/PropertiesParserStrategy.java | 8 +- .../easyi18n/io/parser/yaml/YamlMapper.java | 6 +- .../easyi18n/mapper/AbstractMapperTest.java | 6 +- .../easyi18n/mapper/Json5MapperTest.java | 30 ++-- .../easyi18n/mapper/JsonMapperTest.java | 30 ++-- .../easyi18n/mapper/PropertiesMapperTest.java | 145 ++++++++++++++---- .../easyi18n/mapper/YamlMapperTest.java | 30 ++-- 10 files changed, 191 insertions(+), 105 deletions(-) diff --git a/src/main/java/de/marhali/easyi18n/io/parser/json/JsonMapper.java b/src/main/java/de/marhali/easyi18n/io/parser/json/JsonMapper.java index bd4c1bf..328eae7 100644 --- a/src/main/java/de/marhali/easyi18n/io/parser/json/JsonMapper.java +++ b/src/main/java/de/marhali/easyi18n/io/parser/json/JsonMapper.java @@ -4,8 +4,8 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; -import de.marhali.easyi18n.model.Translation; import de.marhali.easyi18n.model.TranslationNode; +import de.marhali.easyi18n.model.TranslationValue; import de.marhali.easyi18n.util.StringUtil; import org.apache.commons.lang.StringEscapeUtils; @@ -30,7 +30,7 @@ public class JsonMapper { // Nested element - run recursively read(locale, value.getAsJsonObject(), childNode); } else { - Translation translation = childNode.getValue(); + TranslationValue translation = childNode.getValue(); String content = entry.getValue().isJsonArray() ? JsonArrayMapper.read(value.getAsJsonArray()) @@ -55,7 +55,7 @@ public class JsonMapper { json.add(key, childJson); } } else { - Translation translation = childNode.getValue(); + TranslationValue translation = childNode.getValue(); String content = translation.get(locale); if(content != null) { diff --git a/src/main/java/de/marhali/easyi18n/io/parser/json5/Json5Mapper.java b/src/main/java/de/marhali/easyi18n/io/parser/json5/Json5Mapper.java index 80b36ae..dc6bdfe 100644 --- a/src/main/java/de/marhali/easyi18n/io/parser/json5/Json5Mapper.java +++ b/src/main/java/de/marhali/easyi18n/io/parser/json5/Json5Mapper.java @@ -1,12 +1,12 @@ package de.marhali.easyi18n.io.parser.json5; -import de.marhali.easyi18n.model.Translation; import de.marhali.easyi18n.model.TranslationNode; +import de.marhali.easyi18n.model.TranslationValue; import de.marhali.easyi18n.util.StringUtil; - import de.marhali.json5.Json5Element; import de.marhali.json5.Json5Object; import de.marhali.json5.Json5Primitive; + import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.lang.math.NumberUtils; @@ -28,7 +28,7 @@ public class Json5Mapper { // Nested element - run recursively read(locale, value.getAsJson5Object(), childNode); } else { - Translation translation = childNode.getValue(); + TranslationValue translation = childNode.getValue(); String content = value.isJson5Array() ? Json5ArrayMapper.read(value.getAsJson5Array()) @@ -54,7 +54,7 @@ public class Json5Mapper { } } else { - Translation translation = childNode.getValue(); + TranslationValue translation = childNode.getValue(); String content = translation.get(locale); if(content != null) { if(Json5ArrayMapper.isArray(content)) { diff --git a/src/main/java/de/marhali/easyi18n/io/parser/properties/PropertiesMapper.java b/src/main/java/de/marhali/easyi18n/io/parser/properties/PropertiesMapper.java index 0ae62b2..2cac6ae 100644 --- a/src/main/java/de/marhali/easyi18n/io/parser/properties/PropertiesMapper.java +++ b/src/main/java/de/marhali/easyi18n/io/parser/properties/PropertiesMapper.java @@ -1,8 +1,9 @@ package de.marhali.easyi18n.io.parser.properties; -import de.marhali.easyi18n.model.KeyPath; -import de.marhali.easyi18n.model.Translation; import de.marhali.easyi18n.model.TranslationData; +import de.marhali.easyi18n.model.KeyPath; +import de.marhali.easyi18n.model.TranslationValue; +import de.marhali.easyi18n.util.KeyPathConverter; import de.marhali.easyi18n.util.StringUtil; import org.apache.commons.lang.StringEscapeUtils; @@ -16,15 +17,17 @@ import java.util.Map; */ public class PropertiesMapper { - public static void read(String locale, SortableProperties properties, TranslationData data) { + public static void read(String locale, SortableProperties properties, + TranslationData data, KeyPathConverter converter) { + for(Map.Entry entry : properties.entrySet()) { - KeyPath key = new KeyPath(String.valueOf(entry.getKey())); + KeyPath key = converter.fromString(String.valueOf(entry.getKey())); Object value = entry.getValue(); - Translation translation = data.getTranslation(key); + TranslationValue translation = data.getTranslation(key); if(translation == null) { - translation = new Translation(); + translation = new TranslationValue(); } String content = value instanceof String[] @@ -36,12 +39,14 @@ public class PropertiesMapper { } } - public static void write(String locale, SortableProperties properties, TranslationData data) { - for(KeyPath key : data.getFullKeys()) { - Translation translation = data.getTranslation(key); + public static void write(String locale, SortableProperties properties, + TranslationData data, KeyPathConverter converter) { - if(translation != null && translation.containsKey(locale)) { - String simpleKey = key.toSimpleString(); + for(KeyPath key : data.getFullKeys()) { + TranslationValue translation = data.getTranslation(key); + + if(translation != null && translation.containsLocale(locale)) { + String simpleKey = converter.toString(key); String content = translation.get(locale); if(PropertiesArrayMapper.isArray(content)) { diff --git a/src/main/java/de/marhali/easyi18n/io/parser/properties/PropertiesParserStrategy.java b/src/main/java/de/marhali/easyi18n/io/parser/properties/PropertiesParserStrategy.java index 8f29534..22e7722 100644 --- a/src/main/java/de/marhali/easyi18n/io/parser/properties/PropertiesParserStrategy.java +++ b/src/main/java/de/marhali/easyi18n/io/parser/properties/PropertiesParserStrategy.java @@ -7,6 +7,7 @@ import de.marhali.easyi18n.model.TranslationData; import de.marhali.easyi18n.model.TranslationFile; import de.marhali.easyi18n.model.TranslationNode; import de.marhali.easyi18n.settings.ProjectSettings; +import de.marhali.easyi18n.util.KeyPathConverter; import org.jetbrains.annotations.NotNull; @@ -20,8 +21,11 @@ import java.io.StringWriter; */ public class PropertiesParserStrategy extends ParserStrategy { + private final @NotNull KeyPathConverter converter; + public PropertiesParserStrategy(@NotNull ProjectSettings settings) { super(settings); + this.converter = new KeyPathConverter(settings); } @Override @@ -35,7 +39,7 @@ public class PropertiesParserStrategy extends ParserStrategy { try(Reader reader = new InputStreamReader(vf.getInputStream(), vf.getCharset())) { SortableProperties input = new SortableProperties(this.settings.isSorting()); input.load(reader); - PropertiesMapper.read(file.getLocale(), input, targetData); + PropertiesMapper.read(file.getLocale(), input, targetData, converter); } } @@ -45,7 +49,7 @@ public class PropertiesParserStrategy extends ParserStrategy { TranslationData targetData = new TranslationData(data.getLocales(), targetNode); SortableProperties output = new SortableProperties(this.settings.isSorting()); - PropertiesMapper.write(file.getLocale(), output, targetData); + PropertiesMapper.write(file.getLocale(), output, targetData, converter); try(StringWriter writer = new StringWriter()) { output.store(writer, null); diff --git a/src/main/java/de/marhali/easyi18n/io/parser/yaml/YamlMapper.java b/src/main/java/de/marhali/easyi18n/io/parser/yaml/YamlMapper.java index 0ceb5ac..09e92da 100644 --- a/src/main/java/de/marhali/easyi18n/io/parser/yaml/YamlMapper.java +++ b/src/main/java/de/marhali/easyi18n/io/parser/yaml/YamlMapper.java @@ -1,7 +1,7 @@ package de.marhali.easyi18n.io.parser.yaml; -import de.marhali.easyi18n.model.Translation; import de.marhali.easyi18n.model.TranslationNode; +import de.marhali.easyi18n.model.TranslationValue; import de.marhali.easyi18n.util.StringUtil; import org.apache.commons.lang.StringEscapeUtils; @@ -28,7 +28,7 @@ public class YamlMapper { // Nested element - run recursively read(locale, (MapSection) value, childNode); } else { - Translation translation = childNode.getValue(); + TranslationValue translation = childNode.getValue(); String content = value instanceof ListSection ? YamlArrayMapper.read((ListSection) value) @@ -53,7 +53,7 @@ public class YamlMapper { section.setInScope(key, childSection); } } else { - Translation translation = childNode.getValue(); + TranslationValue translation = childNode.getValue(); String content = translation.get(locale); if(content != null) { diff --git a/src/test/java/de/marhali/easyi18n/mapper/AbstractMapperTest.java b/src/test/java/de/marhali/easyi18n/mapper/AbstractMapperTest.java index 9771660..c031e88 100644 --- a/src/test/java/de/marhali/easyi18n/mapper/AbstractMapperTest.java +++ b/src/test/java/de/marhali/easyi18n/mapper/AbstractMapperTest.java @@ -1,6 +1,6 @@ package de.marhali.easyi18n.mapper; -import de.marhali.easyi18n.model.Translation; +import de.marhali.easyi18n.model.TranslationValue; import org.junit.Test; @@ -39,7 +39,7 @@ public abstract class AbstractMapperTest { @Test public abstract void testNumbers(); - protected Translation create(String content) { - return new Translation("en", content); + protected TranslationValue create(String content) { + return new TranslationValue("en", content); } } \ No newline at end of file diff --git a/src/test/java/de/marhali/easyi18n/mapper/Json5MapperTest.java b/src/test/java/de/marhali/easyi18n/mapper/Json5MapperTest.java index befdc04..6be4c79 100644 --- a/src/test/java/de/marhali/easyi18n/mapper/Json5MapperTest.java +++ b/src/test/java/de/marhali/easyi18n/mapper/Json5MapperTest.java @@ -3,8 +3,8 @@ package de.marhali.easyi18n.mapper; import de.marhali.easyi18n.io.parser.json.JsonArrayMapper; import de.marhali.easyi18n.io.parser.json5.Json5ArrayMapper; import de.marhali.easyi18n.io.parser.json5.Json5Mapper; -import de.marhali.easyi18n.model.KeyPath; import de.marhali.easyi18n.model.TranslationData; +import de.marhali.easyi18n.model.KeyPath; import de.marhali.json5.Json5Object; import de.marhali.json5.Json5Primitive; @@ -58,8 +58,8 @@ public class Json5MapperTest extends AbstractMapperTest { @Override public void testArrays() { TranslationData data = new TranslationData(true); - data.setTranslation(KeyPath.of("simple"), create(arraySimple)); - data.setTranslation(KeyPath.of("escaped"), create(arrayEscaped)); + data.setTranslation(new KeyPath("simple"), create(arraySimple)); + data.setTranslation(new KeyPath("escaped"), create(arrayEscaped)); Json5Object output = new Json5Object(); Json5Mapper.write("en", output, data.getRootNode()); @@ -72,14 +72,14 @@ public class Json5MapperTest extends AbstractMapperTest { TranslationData input = new TranslationData(true); Json5Mapper.read("en", output, input.getRootNode()); - Assert.assertTrue(JsonArrayMapper.isArray(input.getTranslation(KeyPath.of("simple")).get("en"))); - Assert.assertTrue(JsonArrayMapper.isArray(input.getTranslation(KeyPath.of("escaped")).get("en"))); + Assert.assertTrue(JsonArrayMapper.isArray(input.getTranslation(new KeyPath("simple")).get("en"))); + Assert.assertTrue(JsonArrayMapper.isArray(input.getTranslation(new KeyPath("escaped")).get("en"))); } @Override public void testSpecialCharacters() { TranslationData data = new TranslationData(true); - data.setTranslation(KeyPath.of("chars"), create(specialCharacters)); + data.setTranslation(new KeyPath("chars"), create(specialCharacters)); Json5Object output = new Json5Object(); Json5Mapper.write("en", output, data.getRootNode()); @@ -90,13 +90,13 @@ public class Json5MapperTest extends AbstractMapperTest { Json5Mapper.read("en", output, input.getRootNode()); Assert.assertEquals(specialCharacters, - StringEscapeUtils.unescapeJava(input.getTranslation(KeyPath.of("chars")).get("en"))); + StringEscapeUtils.unescapeJava(input.getTranslation(new KeyPath("chars")).get("en"))); } @Override public void testNestedKeys() { TranslationData data = new TranslationData(true); - data.setTranslation(KeyPath.of("nested", "key", "section"), create("test")); + data.setTranslation(new KeyPath("nested", "key", "section"), create("test")); Json5Object output = new Json5Object(); Json5Mapper.write("en", output, data.getRootNode()); @@ -106,13 +106,13 @@ public class Json5MapperTest extends AbstractMapperTest { TranslationData input = new TranslationData(true); Json5Mapper.read("en", output, input.getRootNode()); - Assert.assertEquals("test", input.getTranslation(KeyPath.of("nested", "key", "section")).get("en")); + Assert.assertEquals("test", input.getTranslation(new KeyPath("nested", "key", "section")).get("en")); } @Override public void testNonNestedKeys() { TranslationData data = new TranslationData(true); - data.setTranslation(KeyPath.of("long.key.with.many.sections"), create("test")); + data.setTranslation(new KeyPath("long.key.with.many.sections"), create("test")); Json5Object output = new Json5Object(); Json5Mapper.write("en", output, data.getRootNode()); @@ -122,13 +122,13 @@ public class Json5MapperTest extends AbstractMapperTest { TranslationData input = new TranslationData(true); Json5Mapper.read("en", output, input.getRootNode()); - Assert.assertEquals("test", input.getTranslation(KeyPath.of("long.key.with.many.sections")).get("en")); + Assert.assertEquals("test", input.getTranslation(new KeyPath("long.key.with.many.sections")).get("en")); } @Override public void testLeadingSpace() { TranslationData data = new TranslationData(true); - data.setTranslation(KeyPath.of("space"), create(leadingSpace)); + data.setTranslation(new KeyPath("space"), create(leadingSpace)); Json5Object output = new Json5Object(); Json5Mapper.write("en", output, data.getRootNode()); @@ -138,13 +138,13 @@ public class Json5MapperTest extends AbstractMapperTest { TranslationData input = new TranslationData(true); Json5Mapper.read("en", output, input.getRootNode()); - Assert.assertEquals(leadingSpace, input.getTranslation(KeyPath.of("space")).get("en")); + Assert.assertEquals(leadingSpace, input.getTranslation(new KeyPath("space")).get("en")); } @Override public void testNumbers() { TranslationData data = new TranslationData(true); - data.setTranslation(KeyPath.of("numbered"), create("15000")); + data.setTranslation(new KeyPath("numbered"), create("15000")); Json5Object output = new Json5Object(); Json5Mapper.write("en", output, data.getRootNode()); @@ -155,6 +155,6 @@ public class Json5MapperTest extends AbstractMapperTest { input.addProperty("numbered", 143.23); Json5Mapper.read("en", input, data.getRootNode()); - Assert.assertEquals("143.23", data.getTranslation(KeyPath.of("numbered")).get("en")); + Assert.assertEquals("143.23", data.getTranslation(new KeyPath("numbered")).get("en")); } } \ No newline at end of file diff --git a/src/test/java/de/marhali/easyi18n/mapper/JsonMapperTest.java b/src/test/java/de/marhali/easyi18n/mapper/JsonMapperTest.java index 78be880..7647671 100644 --- a/src/test/java/de/marhali/easyi18n/mapper/JsonMapperTest.java +++ b/src/test/java/de/marhali/easyi18n/mapper/JsonMapperTest.java @@ -6,9 +6,9 @@ import com.google.gson.JsonPrimitive; import de.marhali.easyi18n.io.parser.json.JsonArrayMapper; import de.marhali.easyi18n.io.parser.json.JsonMapper; -import de.marhali.easyi18n.model.KeyPath; import de.marhali.easyi18n.model.TranslationData; +import de.marhali.easyi18n.model.KeyPath; import org.apache.commons.lang.StringEscapeUtils; import org.junit.Assert; @@ -59,8 +59,8 @@ public class JsonMapperTest extends AbstractMapperTest { @Override public void testArrays() { TranslationData data = new TranslationData(true); - data.setTranslation(KeyPath.of("simple"), create(arraySimple)); - data.setTranslation(KeyPath.of("escaped"), create(arrayEscaped)); + data.setTranslation(new KeyPath("simple"), create(arraySimple)); + data.setTranslation(new KeyPath("escaped"), create(arrayEscaped)); JsonObject output = new JsonObject(); JsonMapper.write("en", output, data.getRootNode()); @@ -73,14 +73,14 @@ public class JsonMapperTest extends AbstractMapperTest { TranslationData input = new TranslationData(true); JsonMapper.read("en", output, input.getRootNode()); - Assert.assertTrue(JsonArrayMapper.isArray(input.getTranslation(KeyPath.of("simple")).get("en"))); - Assert.assertTrue(JsonArrayMapper.isArray(input.getTranslation(KeyPath.of("escaped")).get("en"))); + Assert.assertTrue(JsonArrayMapper.isArray(input.getTranslation(new KeyPath("simple")).get("en"))); + Assert.assertTrue(JsonArrayMapper.isArray(input.getTranslation(new KeyPath("escaped")).get("en"))); } @Override public void testSpecialCharacters() { TranslationData data = new TranslationData(true); - data.setTranslation(KeyPath.of("chars"), create(specialCharacters)); + data.setTranslation(new KeyPath("chars"), create(specialCharacters)); JsonObject output = new JsonObject(); JsonMapper.write("en", output, data.getRootNode()); @@ -91,13 +91,13 @@ public class JsonMapperTest extends AbstractMapperTest { JsonMapper.read("en", output, input.getRootNode()); Assert.assertEquals(specialCharacters, - StringEscapeUtils.unescapeJava(input.getTranslation(KeyPath.of("chars")).get("en"))); + StringEscapeUtils.unescapeJava(input.getTranslation(new KeyPath("chars")).get("en"))); } @Override public void testNestedKeys() { TranslationData data = new TranslationData(true); - data.setTranslation(KeyPath.of("nested", "key", "section"), create("test")); + data.setTranslation(new KeyPath("nested", "key", "section"), create("test")); JsonObject output = new JsonObject(); JsonMapper.write("en", output, data.getRootNode()); @@ -107,13 +107,13 @@ public class JsonMapperTest extends AbstractMapperTest { TranslationData input = new TranslationData(true); JsonMapper.read("en", output, input.getRootNode()); - Assert.assertEquals("test", input.getTranslation(KeyPath.of("nested", "key", "section")).get("en")); + Assert.assertEquals("test", input.getTranslation(new KeyPath("nested", "key", "section")).get("en")); } @Override public void testNonNestedKeys() { TranslationData data = new TranslationData(true); - data.setTranslation(KeyPath.of("long.key.with.many.sections"), create("test")); + data.setTranslation(new KeyPath("long.key.with.many.sections"), create("test")); JsonObject output = new JsonObject(); JsonMapper.write("en", output, data.getRootNode()); @@ -123,13 +123,13 @@ public class JsonMapperTest extends AbstractMapperTest { TranslationData input = new TranslationData(true); JsonMapper.read("en", output, input.getRootNode()); - Assert.assertEquals("test", input.getTranslation(KeyPath.of("long.key.with.many.sections")).get("en")); + Assert.assertEquals("test", input.getTranslation(new KeyPath("long.key.with.many.sections")).get("en")); } @Override public void testLeadingSpace() { TranslationData data = new TranslationData(true); - data.setTranslation(KeyPath.of("space"), create(leadingSpace)); + data.setTranslation(new KeyPath("space"), create(leadingSpace)); JsonObject output = new JsonObject(); JsonMapper.write("en", output, data.getRootNode()); @@ -139,13 +139,13 @@ public class JsonMapperTest extends AbstractMapperTest { TranslationData input = new TranslationData(true); JsonMapper.read("en", output, input.getRootNode()); - Assert.assertEquals(leadingSpace, input.getTranslation(KeyPath.of("space")).get("en")); + Assert.assertEquals(leadingSpace, input.getTranslation(new KeyPath("space")).get("en")); } @Override public void testNumbers() { TranslationData data = new TranslationData(true); - data.setTranslation(KeyPath.of("numbered"), create("15000")); + data.setTranslation(new KeyPath("numbered"), create("15000")); JsonObject output = new JsonObject(); JsonMapper.write("en", output, data.getRootNode()); @@ -156,6 +156,6 @@ public class JsonMapperTest extends AbstractMapperTest { input.addProperty("numbered", 143.23); JsonMapper.read("en", input, data.getRootNode()); - Assert.assertEquals("143.23", data.getTranslation(KeyPath.of("numbered")).get("en")); + Assert.assertEquals("143.23", data.getTranslation(new KeyPath("numbered")).get("en")); } } \ No newline at end of file diff --git a/src/test/java/de/marhali/easyi18n/mapper/PropertiesMapperTest.java b/src/test/java/de/marhali/easyi18n/mapper/PropertiesMapperTest.java index 2643981..14aa03a 100644 --- a/src/test/java/de/marhali/easyi18n/mapper/PropertiesMapperTest.java +++ b/src/test/java/de/marhali/easyi18n/mapper/PropertiesMapperTest.java @@ -1,12 +1,18 @@ package de.marhali.easyi18n.mapper; +import de.marhali.easyi18n.io.folder.FolderStrategyType; +import de.marhali.easyi18n.io.parser.ParserStrategyType; import de.marhali.easyi18n.io.parser.properties.PropertiesArrayMapper; import de.marhali.easyi18n.io.parser.properties.PropertiesMapper; import de.marhali.easyi18n.io.parser.properties.SortableProperties; -import de.marhali.easyi18n.model.KeyPath; import de.marhali.easyi18n.model.TranslationData; +import de.marhali.easyi18n.model.KeyPath; +import de.marhali.easyi18n.settings.ProjectSettings; +import de.marhali.easyi18n.util.KeyPathConverter; import org.apache.commons.lang.StringEscapeUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.junit.Assert; import java.util.*; @@ -25,10 +31,10 @@ public class PropertiesMapperTest extends AbstractMapperTest { input.setProperty("bravo", "test"); TranslationData data = new TranslationData(false); - PropertiesMapper.read("en", input, data); + PropertiesMapper.read("en", input, data, converter(true)); SortableProperties output = new SortableProperties(false); - PropertiesMapper.write("en", output, data); + PropertiesMapper.write("en", output, data, converter(true)); List expect = Arrays.asList("zulu", "alpha", "bravo"); Assert.assertEquals(expect, new ArrayList<>(output.keySet())); @@ -42,10 +48,10 @@ public class PropertiesMapperTest extends AbstractMapperTest { input.setProperty("bravo", "test"); TranslationData data = new TranslationData(true); - PropertiesMapper.read("en", input, data); + PropertiesMapper.read("en", input, data, converter(true)); SortableProperties output = new SortableProperties(true); - PropertiesMapper.write("en", output, data); + PropertiesMapper.write("en", output, data, converter(true)); List expect = Arrays.asList("alpha", "bravo", "zulu"); Assert.assertEquals(expect, new ArrayList<>(output.keySet())); @@ -54,11 +60,11 @@ public class PropertiesMapperTest extends AbstractMapperTest { @Override public void testArrays() { TranslationData data = new TranslationData(true); - data.setTranslation(KeyPath.of("simple"), create(arraySimple)); - data.setTranslation(KeyPath.of("escaped"), create(arrayEscaped)); + data.setTranslation(new KeyPath("simple"), create(arraySimple)); + data.setTranslation(new KeyPath("escaped"), create(arrayEscaped)); SortableProperties output = new SortableProperties(true); - PropertiesMapper.write("en", output, data); + PropertiesMapper.write("en", output, data, converter(true)); Assert.assertTrue(output.get("simple") instanceof String[]); Assert.assertEquals(arraySimple, PropertiesArrayMapper.read((String[]) output.get("simple"))); @@ -66,93 +72,164 @@ public class PropertiesMapperTest extends AbstractMapperTest { Assert.assertEquals(arrayEscaped, StringEscapeUtils.unescapeJava(PropertiesArrayMapper.read((String[]) output.get("escaped")))); TranslationData input = new TranslationData(true); - PropertiesMapper.read("en", output, input); + PropertiesMapper.read("en", output, input, converter(true)); - Assert.assertTrue(PropertiesArrayMapper.isArray(input.getTranslation(KeyPath.of("simple")).get("en"))); - Assert.assertTrue(PropertiesArrayMapper.isArray(input.getTranslation(KeyPath.of("escaped")).get("en"))); + Assert.assertTrue(PropertiesArrayMapper.isArray(input.getTranslation(new KeyPath("simple")).get("en"))); + Assert.assertTrue(PropertiesArrayMapper.isArray(input.getTranslation(new KeyPath("escaped")).get("en"))); } @Override public void testSpecialCharacters() { TranslationData data = new TranslationData(true); - data.setTranslation(KeyPath.of("chars"), create(specialCharacters)); + data.setTranslation(new KeyPath("chars"), create(specialCharacters)); SortableProperties output = new SortableProperties(true); - PropertiesMapper.write("en", output, data); + PropertiesMapper.write("en", output, data, converter(true)); Assert.assertEquals(specialCharacters, output.get("chars")); TranslationData input = new TranslationData(true); - PropertiesMapper.read("en", output, input); + PropertiesMapper.read("en", output, input, converter(true)); - Assert.assertEquals(specialCharacters, StringEscapeUtils.unescapeJava(input.getTranslation(KeyPath.of("chars")).get("en"))); + Assert.assertEquals(specialCharacters, StringEscapeUtils.unescapeJava(input.getTranslation(new KeyPath("chars")).get("en"))); } @Override public void testNestedKeys() { TranslationData data = new TranslationData(true); - data.setTranslation(KeyPath.of("nested", "key", "sections"), create("test")); + data.setTranslation(new KeyPath("nested", "key", "sections"), create("test")); SortableProperties output = new SortableProperties(true); - PropertiesMapper.write("en", output, data); + PropertiesMapper.write("en", output, data, converter(true)); - Assert.assertEquals("test", output.get("nested.key.sections")); + Assert.assertEquals("test", output.get("nested:key.sections")); TranslationData input = new TranslationData(true); - PropertiesMapper.read("en", output, input); - - System.out.println(input); + PropertiesMapper.read("en", output, input, converter(true)); Assert.assertTrue(input.getRootNode().getChildren().containsKey("nested")); - Assert.assertEquals("test", input.getTranslation(KeyPath.of("nested", "key", "sections")).get("en")); + Assert.assertEquals("test", input.getTranslation(new KeyPath("nested", "key", "sections")).get("en")); } @Override public void testNonNestedKeys() { // Note: Key nesting is not supported in properties file. TranslationData data = new TranslationData(true); - data.setTranslation(KeyPath.of("long.key.with.many.sections"), create("test")); + data.setTranslation(new KeyPath("long.key.with.many.sections"), create("test")); SortableProperties output = new SortableProperties(true); - PropertiesMapper.write("en", output, data); + PropertiesMapper.write("en", output, data, converter(false)); Assert.assertNotNull(output.get("long.key.with.many.sections")); TranslationData input = new TranslationData(true); - PropertiesMapper.read("en", output, input); + PropertiesMapper.read("en", output, input, converter(false)); - Assert.assertEquals("test", input.getTranslation(KeyPath.of("long", "key", "with", "many", "sections")).get("en")); + Assert.assertEquals("test", input.getTranslation(new KeyPath("long.key.with.many.sections")).get("en")); } @Override public void testLeadingSpace() { TranslationData data = new TranslationData(true); - data.setTranslation(KeyPath.of("space"), create(leadingSpace)); + data.setTranslation(new KeyPath("space"), create(leadingSpace)); SortableProperties output = new SortableProperties(true); - PropertiesMapper.write("en", output, data); + PropertiesMapper.write("en", output, data, converter()); Assert.assertEquals(leadingSpace, output.get("space")); TranslationData input = new TranslationData(true); - PropertiesMapper.read("en", output, input); + PropertiesMapper.read("en", output, input, converter()); - Assert.assertEquals(leadingSpace, input.getTranslation(KeyPath.of("space")).get("en")); + Assert.assertEquals(leadingSpace, input.getTranslation(new KeyPath("space")).get("en")); } @Override public void testNumbers() { TranslationData data = new TranslationData(true); - data.setTranslation(KeyPath.of("numbered"), create("15000")); + data.setTranslation(new KeyPath("numbered"), create("15000")); SortableProperties output = new SortableProperties(true); - PropertiesMapper.write("en", output, data); + PropertiesMapper.write("en", output, data, converter()); Assert.assertEquals(15000, output.get("numbered")); SortableProperties input = new SortableProperties(true); input.put("numbered", 143.23); - PropertiesMapper.read("en", input, data); + PropertiesMapper.read("en", input, data, converter()); - Assert.assertEquals("143.23", data.getTranslation(KeyPath.of("numbered")).get("en")); + Assert.assertEquals("143.23", data.getTranslation(new KeyPath("numbered")).get("en")); + } + + private KeyPathConverter converter() { + return converter(true); + } + + private KeyPathConverter converter(boolean nestKeys) { + return new KeyPathConverter(new ProjectSettings() { + @Override + public @Nullable String getLocalesDirectory() { + return null; + } + + @Override + public @NotNull FolderStrategyType getFolderStrategy() { + return FolderStrategyType.MODULARIZED_NAMESPACE; + } + + @Override + public @NotNull ParserStrategyType getParserStrategy() { + return ParserStrategyType.PROPERTIES; + } + + @Override + public @NotNull String getFilePattern() { + return null; + } + + @Override + public boolean isSorting() { + return true; + } + + @Override + public @Nullable String getNamespaceDelimiter() { + return ":"; + } + + @Override + public @NotNull String getSectionDelimiter() { + return "."; + } + + @Override + public @Nullable String getContextDelimiter() { + return null; + } + + @Override + public @Nullable String getPluralDelimiter() { + return null; + } + + @Override + public @Nullable String getDefaultNamespace() { + return null; + } + + @Override + public @NotNull String getPreviewLocale() { + return null; + } + + @Override + public boolean isNestedKeys() { + return nestKeys; + } + + @Override + public boolean isAssistance() { + return false; + } + }); } } \ No newline at end of file diff --git a/src/test/java/de/marhali/easyi18n/mapper/YamlMapperTest.java b/src/test/java/de/marhali/easyi18n/mapper/YamlMapperTest.java index 2cb9d01..9eefe5c 100644 --- a/src/test/java/de/marhali/easyi18n/mapper/YamlMapperTest.java +++ b/src/test/java/de/marhali/easyi18n/mapper/YamlMapperTest.java @@ -2,8 +2,8 @@ package de.marhali.easyi18n.mapper; import de.marhali.easyi18n.io.parser.yaml.YamlArrayMapper; import de.marhali.easyi18n.io.parser.yaml.YamlMapper; -import de.marhali.easyi18n.model.KeyPath; import de.marhali.easyi18n.model.TranslationData; +import de.marhali.easyi18n.model.KeyPath; import org.apache.commons.lang.StringEscapeUtils; import org.junit.Assert; @@ -58,8 +58,8 @@ public class YamlMapperTest extends AbstractMapperTest { @Override public void testArrays() { TranslationData data = new TranslationData(true); - data.setTranslation(KeyPath.of("simple"), create(arraySimple)); - data.setTranslation(KeyPath.of("escaped"), create(arrayEscaped)); + data.setTranslation(new KeyPath("simple"), create(arraySimple)); + data.setTranslation(new KeyPath("escaped"), create(arrayEscaped)); Section output = new MapSection(); YamlMapper.write("en", output, data.getRootNode()); @@ -72,14 +72,14 @@ public class YamlMapperTest extends AbstractMapperTest { TranslationData input = new TranslationData(true); YamlMapper.read("en", output, input.getRootNode()); - Assert.assertTrue(YamlArrayMapper.isArray(input.getTranslation(KeyPath.of("simple")).get("en"))); - Assert.assertTrue(YamlArrayMapper.isArray(input.getTranslation(KeyPath.of("escaped")).get("en"))); + Assert.assertTrue(YamlArrayMapper.isArray(input.getTranslation(new KeyPath("simple")).get("en"))); + Assert.assertTrue(YamlArrayMapper.isArray(input.getTranslation(new KeyPath("escaped")).get("en"))); } @Override public void testSpecialCharacters() { TranslationData data = new TranslationData(true); - data.setTranslation(KeyPath.of("chars"), create(specialCharacters)); + data.setTranslation(new KeyPath("chars"), create(specialCharacters)); Section output = new MapSection(); YamlMapper.write("en", output, data.getRootNode()); @@ -90,13 +90,13 @@ public class YamlMapperTest extends AbstractMapperTest { YamlMapper.read("en", output, input.getRootNode()); Assert.assertEquals(specialCharacters, - StringEscapeUtils.unescapeJava(input.getTranslation(KeyPath.of("chars")).get("en"))); + StringEscapeUtils.unescapeJava(input.getTranslation(new KeyPath("chars")).get("en"))); } @Override public void testNestedKeys() { TranslationData data = new TranslationData(true); - data.setTranslation(KeyPath.of("nested", "key", "section"), create("test")); + data.setTranslation(new KeyPath("nested", "key", "section"), create("test")); Section output = new MapSection(); YamlMapper.write("en", output, data.getRootNode()); @@ -106,13 +106,13 @@ public class YamlMapperTest extends AbstractMapperTest { TranslationData input = new TranslationData(true); YamlMapper.read("en", output, input.getRootNode()); - Assert.assertEquals("test", input.getTranslation(KeyPath.of("nested", "key", "section")).get("en")); + Assert.assertEquals("test", input.getTranslation(new KeyPath("nested", "key", "section")).get("en")); } @Override public void testNonNestedKeys() { TranslationData data = new TranslationData(true); - data.setTranslation(KeyPath.of("long.key.with.many.sections"), create("test")); + data.setTranslation(new KeyPath("long.key.with.many.sections"), create("test")); Section output = new MapSection(); YamlMapper.write("en", output, data.getRootNode()); @@ -122,13 +122,13 @@ public class YamlMapperTest extends AbstractMapperTest { TranslationData input = new TranslationData(true); YamlMapper.read("en", output, input.getRootNode()); - Assert.assertEquals("test", input.getTranslation(KeyPath.of("long.key.with.many.sections")).get("en")); + Assert.assertEquals("test", input.getTranslation(new KeyPath("long.key.with.many.sections")).get("en")); } @Override public void testLeadingSpace() { TranslationData data = new TranslationData(true); - data.setTranslation(KeyPath.of("space"), create(leadingSpace)); + data.setTranslation(new KeyPath("space"), create(leadingSpace)); Section output = new MapSection(); YamlMapper.write("en", output, data.getRootNode()); @@ -138,13 +138,13 @@ public class YamlMapperTest extends AbstractMapperTest { TranslationData input = new TranslationData(true); YamlMapper.read("en", output, input.getRootNode()); - Assert.assertEquals(leadingSpace, input.getTranslation(KeyPath.of("space")).get("en")); + Assert.assertEquals(leadingSpace, input.getTranslation(new KeyPath("space")).get("en")); } @Override public void testNumbers() { TranslationData data = new TranslationData(true); - data.setTranslation(KeyPath.of("numbered"), create("15000")); + data.setTranslation(new KeyPath("numbered"), create("15000")); Section output = new MapSection(); YamlMapper.write("en", output, data.getRootNode()); @@ -155,6 +155,6 @@ public class YamlMapperTest extends AbstractMapperTest { input.set("numbered", 143.23); YamlMapper.read("en", input, data.getRootNode()); - Assert.assertEquals("143.23", data.getTranslation(KeyPath.of("numbered")).get("en")); + Assert.assertEquals("143.23", data.getTranslation(new KeyPath("numbered")).get("en")); } } \ No newline at end of file From 6887a4e416c03a015a768753a000c5afd167face Mon Sep 17 00:00:00 2001 From: marhali Date: Mon, 11 Apr 2022 22:07:45 +0200 Subject: [PATCH 34/51] remove description for now --- .../easyi18n/dialog/TranslationDialog.java | 6 +- .../easyi18n/model/TranslationValue.java | 112 ++++-------------- 2 files changed, 25 insertions(+), 93 deletions(-) diff --git a/src/main/java/de/marhali/easyi18n/dialog/TranslationDialog.java b/src/main/java/de/marhali/easyi18n/dialog/TranslationDialog.java index d60c74f..5d69681 100644 --- a/src/main/java/de/marhali/easyi18n/dialog/TranslationDialog.java +++ b/src/main/java/de/marhali/easyi18n/dialog/TranslationDialog.java @@ -37,7 +37,6 @@ abstract class TranslationDialog { protected final @NotNull Translation origin; protected final JTextField keyField; - protected final JTextField descriptionField; protected final Map localeValueFields; /** @@ -55,7 +54,6 @@ abstract class TranslationDialog { TranslationValue value = origin.getValue(); this.keyField = new JBTextField(converter.toString(origin.getKey())); - this.descriptionField = new JBTextField(value != null ? value.getDescription() : null); this.localeValueFields = new HashMap<>(); for(String locale : InstanceManager.get(project).store().getData().getLocales()) { @@ -92,9 +90,8 @@ abstract class TranslationDialog { */ protected @NotNull Translation getState() { KeyPath key = converter.fromString(keyField.getText()); - TranslationValue value = new TranslationValue(); - value.setDescription(descriptionField.getText()); + TranslationValue value = new TranslationValue(); for(Map.Entry entry : localeValueFields.entrySet()) { value.put(entry.getKey(), entry.getValue().getText()); @@ -106,7 +103,6 @@ abstract class TranslationDialog { private DialogBuilder createDialog() { JPanel panel = FormBuilder.createFormBuilder() .addLabeledComponent(bundle.getString("translation.key"), keyField, true) - .addLabeledComponent(bundle.getString("translation.description"), descriptionField, 6, true) .addComponent(createLocalesPanel(), 12) .getPanel(); diff --git a/src/main/java/de/marhali/easyi18n/model/TranslationValue.java b/src/main/java/de/marhali/easyi18n/model/TranslationValue.java index 6abd17b..6ca10bd 100644 --- a/src/main/java/de/marhali/easyi18n/model/TranslationValue.java +++ b/src/main/java/de/marhali/easyi18n/model/TranslationValue.java @@ -6,6 +6,7 @@ import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.HashMap; import java.util.Map; +import java.util.Set; /** * Represents the set values behind a specific translation. @@ -13,122 +14,57 @@ import java.util.Map; */ public class TranslationValue { - private @Nullable String description; - private @NotNull Map values; - private @Nullable Object misc; - - public TranslationValue(@Nullable String description, @NotNull Map values, @Nullable Object misc) { - this.description = description; - this.values = values; - this.misc = misc; - } - - public TranslationValue(@NotNull Map values) { - this(null, values, null); - } - - public TranslationValue(@NotNull String locale, @NotNull String value) { - this(new HashMap<>(Map.of(locale, value))); - } + private @NotNull Map localeValues; public TranslationValue() { - this(null, new HashMap<>(), null); + this.localeValues = new HashMap<>(); } - /** - * Retrieve additional description for this translation - * @return Description - */ - public @Nullable String getDescription() { - return description; + public TranslationValue(@NotNull String locale, @NotNull String content) { + this(); + localeValues.put(locale, content); } - /** - * Override or set description for this translation - * @param description Description - */ - public void setDescription(@Nullable String description) { - this.description = description; + public Set> getEntries() { + return this.localeValues.entrySet(); } - /** - * Set locale specific values. - * @param values New values - */ - public void setValues(@NotNull Map values) { - this.values = values; + public Collection getLocaleContents() { + return this.localeValues.values(); } - public @NotNull Map getValues() { - return values; + public void setLocaleValues(@NotNull Map localeValues) { + this.localeValues = localeValues; } - /** - * Overrides or sets a value for a specific locale. - * @param locale Locale type - * @param value New value - */ - public void put(@NotNull String locale, @Nullable String value) { - if(value == null) { // Delete operation - values.remove(locale); - } else { - values.put(locale, value); - } - } - - public void remove(String locale) { - values.remove(locale); - } - - /** - * Retrieves the associated value for a specific locale - * @param locale Locale type - * @return Value or null if missing - */ public @Nullable String get(@NotNull String locale) { - return values.get(locale); + return this.localeValues.get(locale); } - public boolean containsLocale(@Nullable String locale) { - return values.containsKey(locale); + public void put(@NotNull String locale, @NotNull String content) { + this.localeValues.put(locale, content); } - public @NotNull Collection getLocaleValues() { - return values.values(); + public void remove(@NotNull String locale) { + this.localeValues.remove(locale); + } + + public boolean containsLocale(@NotNull String locale) { + return this.localeValues.containsKey(locale); } public int size() { - return values.size(); + return this.localeValues.size(); } public void clear() { - description = null; - values.clear(); - misc = null; - } - - /** - * I18n support data - * @return Data - */ - public @Nullable Object getMisc() { - return misc; - } - - /** - * Set or update I18n support data - * @param misc New Data - */ - public void setMisc(@Nullable Object misc) { - this.misc = misc; + this.localeValues.clear(); } @Override public String toString() { return "TranslationValue{" + - "description='" + description + '\'' + - ", values=" + values + - ", misc=" + misc + + "localeValues=" + localeValues + '}'; } } From df3d5d8dfdb48075e537e1002fecae6af07d7cbd Mon Sep 17 00:00:00 2001 From: marhali Date: Mon, 11 Apr 2022 22:10:45 +0200 Subject: [PATCH 35/51] update to new data structure --- .../tabs/mapper/TableModelMapper.java | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/main/java/de/marhali/easyi18n/tabs/mapper/TableModelMapper.java b/src/main/java/de/marhali/easyi18n/tabs/mapper/TableModelMapper.java index 18137c2..62760cd 100644 --- a/src/main/java/de/marhali/easyi18n/tabs/mapper/TableModelMapper.java +++ b/src/main/java/de/marhali/easyi18n/tabs/mapper/TableModelMapper.java @@ -1,8 +1,13 @@ package de.marhali.easyi18n.tabs.mapper; -import de.marhali.easyi18n.model.*; +import de.marhali.easyi18n.model.TranslationData; +import de.marhali.easyi18n.model.action.TranslationUpdate; import de.marhali.easyi18n.model.bus.FilterMissingTranslationsListener; import de.marhali.easyi18n.model.bus.SearchQueryListener; +import de.marhali.easyi18n.model.KeyPath; +import de.marhali.easyi18n.model.Translation; +import de.marhali.easyi18n.model.TranslationValue; +import de.marhali.easyi18n.util.KeyPathConverter; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; @@ -50,10 +55,10 @@ public class TableModelMapper implements TableModel, SearchQueryListener, Filter List matches = new ArrayList<>(); for(KeyPath key : this.data.getFullKeys()) { - if(this.converter.concat(key).toLowerCase().contains(query)) { + if(this.converter.toString(key).toLowerCase().contains(query)) { matches.add(key); } else { - for(String content : this.data.getTranslation(key).values()) { + for(String content : this.data.getTranslation(key).getLocaleContents()) { if(content.toLowerCase().contains(query)) { matches.add(key); } @@ -74,7 +79,7 @@ public class TableModelMapper implements TableModel, SearchQueryListener, Filter List matches = new ArrayList<>(); for(KeyPath key : this.data.getFullKeys()) { - if(this.data.getTranslation(key).values().size() != this.locales.size()) { + if(this.data.getTranslation(key).getLocaleContents().size() != this.locales.size()) { matches.add(key); } } @@ -117,25 +122,25 @@ public class TableModelMapper implements TableModel, SearchQueryListener, Filter KeyPath key = this.fullKeys.get(rowIndex); if(columnIndex == 0) { // Keys - return this.converter.concat(key); + return this.converter.toString(key); } String locale = this.locales.get(columnIndex - 1); - Translation translation = this.data.getTranslation(key); + TranslationValue value = this.data.getTranslation(key); - return translation == null ? null : translation.get(locale); + return value == null ? null : value.get(locale); } @Override public void setValueAt(Object aValue, int rowIndex, int columnIndex) { KeyPath key = this.fullKeys.get(rowIndex); - Translation translation = this.data.getTranslation(key); + TranslationValue translation = this.data.getTranslation(key); if(translation == null) { // Unknown cell return; } - KeyPath newKey = columnIndex == 0 ? this.converter.split(String.valueOf(aValue)) : key; + KeyPath newKey = columnIndex == 0 ? this.converter.fromString(String.valueOf(aValue)) : key; // Translation content update if(columnIndex > 0) { @@ -146,8 +151,8 @@ public class TableModelMapper implements TableModel, SearchQueryListener, Filter } } - TranslationUpdate update = new TranslationUpdate(new KeyedTranslation(key, translation), - new KeyedTranslation(newKey, translation)); + TranslationUpdate update = new TranslationUpdate(new Translation(key, translation), + new Translation(newKey, translation)); this.updater.accept(update); } From da7f5f56638b18d62d3cf539e4fea369ee9882c7 Mon Sep 17 00:00:00 2001 From: marhali Date: Mon, 11 Apr 2022 22:12:03 +0200 Subject: [PATCH 36/51] update to new data structure --- .../de/marhali/easyi18n/tabs/TableView.java | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/main/java/de/marhali/easyi18n/tabs/TableView.java b/src/main/java/de/marhali/easyi18n/tabs/TableView.java index 02ad3b7..51b65de 100644 --- a/src/main/java/de/marhali/easyi18n/tabs/TableView.java +++ b/src/main/java/de/marhali/easyi18n/tabs/TableView.java @@ -5,14 +5,19 @@ import com.intellij.ui.components.JBScrollPane; import com.intellij.ui.table.JBTable; import de.marhali.easyi18n.InstanceManager; -import de.marhali.easyi18n.listener.ReturnKeyListener; -import de.marhali.easyi18n.model.*; import de.marhali.easyi18n.dialog.EditDialog; +import de.marhali.easyi18n.listener.ReturnKeyListener; import de.marhali.easyi18n.listener.DeleteKeyListener; import de.marhali.easyi18n.listener.PopupClickListener; +import de.marhali.easyi18n.model.TranslationData; +import de.marhali.easyi18n.model.action.TranslationDelete; import de.marhali.easyi18n.model.bus.BusListener; +import de.marhali.easyi18n.model.KeyPath; +import de.marhali.easyi18n.model.Translation; +import de.marhali.easyi18n.model.TranslationValue; import de.marhali.easyi18n.renderer.TableRenderer; import de.marhali.easyi18n.tabs.mapper.TableModelMapper; +import de.marhali.easyi18n.util.KeyPathConverter; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -56,20 +61,20 @@ public class TableView implements BusListener { return; } - KeyPath fullPath = this.converter.split(String.valueOf(this.table.getValueAt(row, 0))); - Translation translation = InstanceManager.get(project).store().getData().getTranslation(fullPath); + KeyPath fullPath = this.converter.fromString(String.valueOf(this.table.getValueAt(row, 0))); + TranslationValue value = InstanceManager.get(project).store().getData().getTranslation(fullPath); - if (translation != null) { - new EditDialog(project, new KeyedTranslation(fullPath, translation)).showAndHandle(); + if (value != null) { + new EditDialog(project, new Translation(fullPath, value)).showAndHandle(); } } private void deleteSelectedRows() { for (int selectedRow : table.getSelectedRows()) { - KeyPath fullPath = this.converter.split(String.valueOf(table.getValueAt(selectedRow, 0))); + KeyPath fullPath = this.converter.fromString(String.valueOf(table.getValueAt(selectedRow, 0))); InstanceManager.get(project).processUpdate( - new TranslationDelete(new KeyedTranslation(fullPath, null)) + new TranslationDelete(new Translation(fullPath, null)) ); } } @@ -84,7 +89,7 @@ public class TableView implements BusListener { @Override public void onFocusKey(@NotNull KeyPath key) { - String concatKey = this.converter.concat(key); + String concatKey = this.converter.toString(key); int row = -1; for (int i = 0; i < table.getRowCount(); i++) { From 8b375cb2017fd1897e6f49b1f0c635445220f655 Mon Sep 17 00:00:00 2001 From: marhali Date: Mon, 11 Apr 2022 22:15:55 +0200 Subject: [PATCH 37/51] update to new data structure --- .../de/marhali/easyi18n/tabs/TreeView.java | 16 +++++++++------ .../easyi18n/tabs/mapper/TreeModelMapper.java | 20 +++++++++++-------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/main/java/de/marhali/easyi18n/tabs/TreeView.java b/src/main/java/de/marhali/easyi18n/tabs/TreeView.java index 670e907..049f516 100644 --- a/src/main/java/de/marhali/easyi18n/tabs/TreeView.java +++ b/src/main/java/de/marhali/easyi18n/tabs/TreeView.java @@ -9,14 +9,18 @@ import com.intellij.ui.components.JBScrollPane; import com.intellij.ui.treeStructure.Tree; import de.marhali.easyi18n.InstanceManager; +import de.marhali.easyi18n.dialog.EditDialog; import de.marhali.easyi18n.listener.ReturnKeyListener; -import de.marhali.easyi18n.model.*; +import de.marhali.easyi18n.model.TranslationData; +import de.marhali.easyi18n.model.action.TranslationDelete; import de.marhali.easyi18n.model.bus.BusListener; import de.marhali.easyi18n.action.treeview.CollapseTreeViewAction; import de.marhali.easyi18n.action.treeview.ExpandTreeViewAction; -import de.marhali.easyi18n.dialog.EditDialog; import de.marhali.easyi18n.listener.DeleteKeyListener; import de.marhali.easyi18n.listener.PopupClickListener; +import de.marhali.easyi18n.model.KeyPath; +import de.marhali.easyi18n.model.Translation; +import de.marhali.easyi18n.model.TranslationValue; import de.marhali.easyi18n.renderer.TreeRenderer; import de.marhali.easyi18n.settings.ProjectSettingsService; import de.marhali.easyi18n.tabs.mapper.TreeModelMapper; @@ -127,13 +131,13 @@ public class TreeView implements BusListener { } KeyPath fullPath = TreeUtil.getFullPath(path); - Translation translation = InstanceManager.get(project).store().getData().getTranslation(fullPath); + TranslationValue value = InstanceManager.get(project).store().getData().getTranslation(fullPath); - if (translation == null) { + if (value == null) { return; } - new EditDialog(project, new KeyedTranslation(fullPath, translation)).showAndHandle(); + new EditDialog(project, new Translation(fullPath, value)).showAndHandle(); } private void deleteSelectedNodes() { @@ -147,7 +151,7 @@ public class TreeView implements BusListener { KeyPath fullPath = TreeUtil.getFullPath(path); InstanceManager.get(project).processUpdate( - new TranslationDelete(new KeyedTranslation(fullPath, null)) + new TranslationDelete(new Translation(fullPath, null)) ); } } diff --git a/src/main/java/de/marhali/easyi18n/tabs/mapper/TreeModelMapper.java b/src/main/java/de/marhali/easyi18n/tabs/mapper/TreeModelMapper.java index e337cc3..18ff29c 100644 --- a/src/main/java/de/marhali/easyi18n/tabs/mapper/TreeModelMapper.java +++ b/src/main/java/de/marhali/easyi18n/tabs/mapper/TreeModelMapper.java @@ -3,10 +3,14 @@ package de.marhali.easyi18n.tabs.mapper; import com.intellij.ide.projectView.PresentationData; import com.intellij.ui.JBColor; -import de.marhali.easyi18n.model.*; +import de.marhali.easyi18n.model.TranslationData; +import de.marhali.easyi18n.model.TranslationNode; import de.marhali.easyi18n.model.bus.FilterMissingTranslationsListener; import de.marhali.easyi18n.model.bus.SearchQueryListener; +import de.marhali.easyi18n.model.KeyPath; +import de.marhali.easyi18n.model.TranslationValue; import de.marhali.easyi18n.settings.ProjectSettings; +import de.marhali.easyi18n.util.KeyPathConverter; import de.marhali.easyi18n.util.UiUtil; import org.jetbrains.annotations.NotNull; @@ -31,7 +35,7 @@ public class TreeModelMapper extends DefaultTreeModel implements SearchQueryList super(null); this.data = data; - this.converter = new KeyPathConverter(state.isNestedKeys()); + this.converter = new KeyPathConverter(state); this.state = state; DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode(); @@ -53,15 +57,15 @@ public class TreeModelMapper extends DefaultTreeModel implements SearchQueryList query = query.toLowerCase(); for(KeyPath currentKey : this.data.getFullKeys()) { - Translation translation = this.data.getTranslation(currentKey); - String loweredKey = this.converter.concat(currentKey).toLowerCase(); + TranslationValue translation = this.data.getTranslation(currentKey); + String loweredKey = this.converter.toString(currentKey).toLowerCase(); if(query.contains(loweredKey) || loweredKey.contains(query)) { shadow.setTranslation(currentKey, translation); continue; } - for(String currentContent : translation.values()) { + for(String currentContent : translation.getLocaleContents()) { if(currentContent.toLowerCase().contains(query)) { shadow.setTranslation(currentKey, translation); break; @@ -85,9 +89,9 @@ public class TreeModelMapper extends DefaultTreeModel implements SearchQueryList } for(KeyPath currentKey : this.data.getFullKeys()) { - Translation translation = this.data.getTranslation(currentKey); + TranslationValue translation = this.data.getTranslation(currentKey); - if(translation.values().size() != this.data.getLocales().size()) { + if(translation.getLocaleContents().size() != this.data.getLocales().size()) { shadow.setTranslation(currentKey, translation); } } @@ -125,7 +129,7 @@ public class TreeModelMapper extends DefaultTreeModel implements SearchQueryList } else { String previewLocale = this.state.getPreviewLocale(); String sub = "(" + previewLocale + ": " + childTranslationNode.getValue().get(previewLocale) + ")"; - String tooltip = UiUtil.generateHtmlTooltip(childTranslationNode.getValue()); + String tooltip = UiUtil.generateHtmlTooltip(childTranslationNode.getValue().getEntries()); PresentationData data = new PresentationData(key, sub, null, null); data.setTooltip(tooltip); From cb228a7c05f4e9c75a01893ecd5619e7d19db042 Mon Sep 17 00:00:00 2001 From: marhali Date: Mon, 11 Apr 2022 22:16:09 +0200 Subject: [PATCH 38/51] update to new data structure --- src/main/java/de/marhali/easyi18n/util/UiUtil.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/de/marhali/easyi18n/util/UiUtil.java b/src/main/java/de/marhali/easyi18n/util/UiUtil.java index 34af963..1b2cbd6 100644 --- a/src/main/java/de/marhali/easyi18n/util/UiUtil.java +++ b/src/main/java/de/marhali/easyi18n/util/UiUtil.java @@ -1,6 +1,7 @@ package de.marhali.easyi18n.util; import java.util.Map; +import java.util.Set; /** * User interface utilities. @@ -13,12 +14,12 @@ public class UiUtil { * @param messages Contains locales with desired translation * @return String with html format */ - public static String generateHtmlTooltip(Map messages) { + public static String generateHtmlTooltip(Set> messages) { StringBuilder builder = new StringBuilder(); builder.append(""); - for(Map.Entry entry : messages.entrySet()) { + for(Map.Entry entry : messages) { builder.append(""); builder.append(entry.getKey()).append(":"); builder.append(" "); From b89bae1daec15b21cab00398b324ad759aafa195 Mon Sep 17 00:00:00 2001 From: marhali Date: Mon, 11 Apr 2022 22:16:45 +0200 Subject: [PATCH 39/51] provide presets --- .../marhali/easyi18n/settings/presets/ReactI18NextPreset.java | 2 +- .../de/marhali/easyi18n/settings/presets/VueI18nPreset.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/de/marhali/easyi18n/settings/presets/ReactI18NextPreset.java b/src/main/java/de/marhali/easyi18n/settings/presets/ReactI18NextPreset.java index 2a76af3..8d5dbeb 100644 --- a/src/main/java/de/marhali/easyi18n/settings/presets/ReactI18NextPreset.java +++ b/src/main/java/de/marhali/easyi18n/settings/presets/ReactI18NextPreset.java @@ -1,7 +1,7 @@ package de.marhali.easyi18n.settings.presets; import de.marhali.easyi18n.io.parser.ParserStrategyType; -import de.marhali.easyi18n.model.FolderStrategyType; +import de.marhali.easyi18n.io.folder.FolderStrategyType; import de.marhali.easyi18n.settings.ProjectSettings; import org.jetbrains.annotations.NotNull; diff --git a/src/main/java/de/marhali/easyi18n/settings/presets/VueI18nPreset.java b/src/main/java/de/marhali/easyi18n/settings/presets/VueI18nPreset.java index 83c3276..ea6d064 100644 --- a/src/main/java/de/marhali/easyi18n/settings/presets/VueI18nPreset.java +++ b/src/main/java/de/marhali/easyi18n/settings/presets/VueI18nPreset.java @@ -1,7 +1,7 @@ package de.marhali.easyi18n.settings.presets; import de.marhali.easyi18n.io.parser.ParserStrategyType; -import de.marhali.easyi18n.model.FolderStrategyType; +import de.marhali.easyi18n.io.folder.FolderStrategyType; import de.marhali.easyi18n.settings.ProjectSettings; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; From f5384f30d9fd3211ef132c1e0d3e971924aa0c8c Mon Sep 17 00:00:00 2001 From: marhali Date: Thu, 14 Apr 2022 15:15:16 +0200 Subject: [PATCH 40/51] add new action translations --- src/main/resources/messages.properties | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties index 5d3cc88..7986043 100644 --- a/src/main/resources/messages.properties +++ b/src/main/resources/messages.properties @@ -1,7 +1,8 @@ -view.tree.title=TreeView +documentation=EasyI18n Translation +view.tree.title=Tree View view.tree.collapse=Collapse Tree view.tree.expand=Expand Tree -view.table.title=TableView +view.table.title=Table View view.empty=No translations found. Click on settings to specify a locales directory or reload action.add=Add Translation action.edit=Edit Translation @@ -10,6 +11,7 @@ action.reload=Reload From Disk action.settings=Settings action.search=Search... action.delete=Delete +action.extract=Extract translation translation.key=Key translation.locales=Locales # Settings From 0d81fc9b1681c00c2066223fcae2b70b85181a65 Mon Sep 17 00:00:00 2001 From: marhali Date: Thu, 14 Apr 2022 15:15:49 +0200 Subject: [PATCH 41/51] define api for optional assistance functionalities --- .../easyi18n/assistance/OptionalAssistance.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/main/java/de/marhali/easyi18n/assistance/OptionalAssistance.java diff --git a/src/main/java/de/marhali/easyi18n/assistance/OptionalAssistance.java b/src/main/java/de/marhali/easyi18n/assistance/OptionalAssistance.java new file mode 100644 index 0000000..3d33b60 --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/assistance/OptionalAssistance.java @@ -0,0 +1,17 @@ +package de.marhali.easyi18n.assistance; + +import com.intellij.openapi.project.Project; + +import de.marhali.easyi18n.settings.ProjectSettingsService; + +import org.jetbrains.annotations.NotNull; + +/** + * Used to define editor hooks as assistable. + * @author marhali + */ +public interface OptionalAssistance { + default boolean isAssistance(@NotNull Project project) { + return ProjectSettingsService.get(project).getState().isAssistance(); + } +} From 25c8f0f3db7115b5dd33997de0017fd97383587e Mon Sep 17 00:00:00 2001 From: marhali Date: Thu, 14 Apr 2022 15:22:12 +0200 Subject: [PATCH 42/51] update changelog --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d18be1..41298bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,9 +8,12 @@ ### Added - Key delimiters (namespace / section) can be configured -- Individual icon to tool-window +- Extract translation intention +- Individual icon for tool-window and lookup items +- Dedicated configuration file (easy-i18n.xml) inside .idea folder ### Changed +- Editor assistance has been reengineered. This will affect key suggestion and annotation - Moved configuration dialog into own page inside IDE Settings ### Fixed From 0a8a92d3f8379801f464163bd6d62f291ac9ef70 Mon Sep 17 00:00:00 2001 From: marhali Date: Thu, 14 Apr 2022 15:23:35 +0200 Subject: [PATCH 43/51] move update process into parent class --- .../de/marhali/easyi18n/dialog/AddDialog.java | 7 +++-- .../marhali/easyi18n/dialog/EditDialog.java | 12 ++++---- .../easyi18n/dialog/TranslationDialog.java | 30 +++++++++++++++---- 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/src/main/java/de/marhali/easyi18n/dialog/AddDialog.java b/src/main/java/de/marhali/easyi18n/dialog/AddDialog.java index be14827..84164e9 100644 --- a/src/main/java/de/marhali/easyi18n/dialog/AddDialog.java +++ b/src/main/java/de/marhali/easyi18n/dialog/AddDialog.java @@ -4,11 +4,11 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogBuilder; import com.intellij.openapi.ui.DialogWrapper; -import de.marhali.easyi18n.InstanceManager; import de.marhali.easyi18n.model.action.TranslationCreate; import de.marhali.easyi18n.model.KeyPath; import de.marhali.easyi18n.model.Translation; import de.marhali.easyi18n.model.TranslationValue; +import de.marhali.easyi18n.model.action.TranslationUpdate; import de.marhali.easyi18n.settings.ProjectSettingsService; import org.jetbrains.annotations.NotNull; @@ -58,9 +58,10 @@ public class AddDialog extends TranslationDialog { } @Override - protected void handleExit(int exitCode) { + protected @Nullable TranslationUpdate handleExit(int exitCode) { if(exitCode == DialogWrapper.OK_EXIT_CODE) { - InstanceManager.get(project).processUpdate(new TranslationCreate(getState())); + return new TranslationCreate(getState()); } + return null; } } diff --git a/src/main/java/de/marhali/easyi18n/dialog/EditDialog.java b/src/main/java/de/marhali/easyi18n/dialog/EditDialog.java index f9ffa69..6c9662c 100644 --- a/src/main/java/de/marhali/easyi18n/dialog/EditDialog.java +++ b/src/main/java/de/marhali/easyi18n/dialog/EditDialog.java @@ -4,13 +4,13 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogBuilder; import com.intellij.openapi.ui.DialogWrapper; -import de.marhali.easyi18n.InstanceManager; import de.marhali.easyi18n.dialog.descriptor.DeleteActionDescriptor; import de.marhali.easyi18n.model.action.TranslationDelete; import de.marhali.easyi18n.model.action.TranslationUpdate; import de.marhali.easyi18n.model.Translation; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; @@ -42,14 +42,14 @@ public class EditDialog extends TranslationDialog { } @Override - protected void handleExit(int exitCode) { + protected @Nullable TranslationUpdate handleExit(int exitCode) { switch (exitCode) { case DialogWrapper.OK_EXIT_CODE: - InstanceManager.get(project).processUpdate(new TranslationUpdate(origin, getState())); - break; + return new TranslationUpdate(origin, getState()); case DeleteActionDescriptor.EXIT_CODE: - InstanceManager.get(project).processUpdate(new TranslationDelete(origin)); - break; + return new TranslationDelete(origin); + default: + return null; } } } \ No newline at end of file diff --git a/src/main/java/de/marhali/easyi18n/dialog/TranslationDialog.java b/src/main/java/de/marhali/easyi18n/dialog/TranslationDialog.java index 5d69681..306e5ba 100644 --- a/src/main/java/de/marhali/easyi18n/dialog/TranslationDialog.java +++ b/src/main/java/de/marhali/easyi18n/dialog/TranslationDialog.java @@ -4,24 +4,25 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogBuilder; import com.intellij.ui.components.JBScrollPane; import com.intellij.ui.components.JBTextField; +import com.intellij.util.Consumer; import com.intellij.util.ui.FormBuilder; import de.marhali.easyi18n.InstanceManager; import de.marhali.easyi18n.model.KeyPath; import de.marhali.easyi18n.model.Translation; import de.marhali.easyi18n.model.TranslationValue; +import de.marhali.easyi18n.model.action.TranslationUpdate; import de.marhali.easyi18n.settings.ProjectSettings; import de.marhali.easyi18n.settings.ProjectSettingsService; import de.marhali.easyi18n.util.KeyPathConverter; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.border.EtchedBorder; import java.awt.*; -import java.util.HashMap; -import java.util.Map; -import java.util.ResourceBundle; +import java.util.*; /** * Base for add and edit translation dialogs. @@ -39,6 +40,8 @@ abstract class TranslationDialog { protected final JTextField keyField; protected final Map localeValueFields; + private final Set> callbacks; + /** * Constructs a new translation dialog. * @param project Opened project @@ -50,6 +53,8 @@ abstract class TranslationDialog { this.converter = new KeyPathConverter(settings); this.origin = origin; + this.callbacks = new HashSet<>(); + // Fields TranslationValue value = origin.getValue(); @@ -61,6 +66,15 @@ abstract class TranslationDialog { } } + /** + * Registers a callback that is called on dialog close with the final state. + * If the user aborts the dialog no callback is called. + * @param callback Callback to register + */ + public void registerCallback(Consumer callback) { + callbacks.add(callback); + } + /** * Implementation needs to configure the dialog. E.g. title, actions, ... * The implementation needs to set the provided centerPanel as the view panel. @@ -72,8 +86,9 @@ abstract class TranslationDialog { /** * Implementation needs to handle exit * @param exitCode See {@link com.intellij.openapi.ui.DialogWrapper} for exit codes + * @return update conclusion, null if aborted */ - protected abstract void handleExit(int exitCode); + protected abstract @Nullable TranslationUpdate handleExit(int exitCode); /** * Opens the translation modal and applies the appropriate logic on modal close. @@ -81,7 +96,12 @@ abstract class TranslationDialog { */ public void showAndHandle() { int exitCode = createDialog().show(); - handleExit(exitCode); + TranslationUpdate update = handleExit(exitCode); + + if(update != null) { + InstanceManager.get(project).processUpdate(update); + callbacks.forEach(callback -> callback.consume(update)); + } } /** From 6e9d0671ca4308cfb0364041046801220cdf0d92 Mon Sep 17 00:00:00 2001 From: marhali Date: Thu, 14 Apr 2022 20:50:45 +0200 Subject: [PATCH 44/51] reengineer editor assistance Tasks Left - language support (vue, js / ts, kotlin, php) - Intention system is bounded to Java ATM --- .../completion/JavaCompletionContributor.java | 16 +++ .../completion/KeyCompletionProvider.java | 60 ++++++++++ .../completion/KtCompletionContributor.java} | 11 +- .../AbstractDocumentationProvider.java | 104 ++++++++++++++++++ .../CommonDocumentationProvider.java | 26 +++++ .../folding/AbstractFoldingBuilder.java | 53 +++++++++ .../folding/JavaFoldingBuilder.java | 66 +++++++++++ .../assistance/folding/KtFoldingBuilder.java | 63 +++++++++++ .../intention/AbstractExtractIntention.java | 93 ++++++++++++++++ .../intention/JavaExtractIntention.java | 35 ++++++ .../intention/JsExtractIntention.java | 40 +++++++ .../intention/KtExtractIntention.java | 41 +++++++ .../AbstractKeyReferenceContributor.java | 53 +++++++++ .../JavaKeyReferenceContributor.java | 40 +++++++ .../reference/JsKeyReferenceContributor.java | 39 +++++++ .../reference/KtKeyReferenceContributor.java | 46 ++++++++ .../assistance/reference/PsiKeyReference.java | 72 ++++++++++++ .../marhali/easyi18n/editor/KeyAnnotator.java | 61 ---------- .../editor/KeyCompletionProvider.java | 64 ----------- .../marhali/easyi18n/editor/KeyReference.java | 93 ---------------- .../editor/generic/GenericFoldingBuilder.java | 87 --------------- .../editor/generic/GenericKeyAnnotator.java | 33 ------ .../GenericKeyCompletionContributor.java | 24 ---- .../GenericKeyReferenceContributor.java | 54 --------- .../editor/kotlin/KotlinKeyAnnotator.java | 31 ------ .../kotlin/KotlinKeyReferenceContributor.java | 59 ---------- .../META-INF/de.marhali.easyi18n-java.xml | 24 +++- .../de.marhali.easyi18n-javascript.xml | 15 ++- .../META-INF/de.marhali.easyi18n-kotlin.xml | 26 ++++- src/main/resources/META-INF/plugin.xml | 9 -- 30 files changed, 908 insertions(+), 530 deletions(-) create mode 100644 src/main/java/de/marhali/easyi18n/assistance/completion/JavaCompletionContributor.java create mode 100644 src/main/java/de/marhali/easyi18n/assistance/completion/KeyCompletionProvider.java rename src/main/java/de/marhali/easyi18n/{editor/kotlin/KotlinKeyCompletionContributor.java => assistance/completion/KtCompletionContributor.java} (60%) create mode 100644 src/main/java/de/marhali/easyi18n/assistance/documentation/AbstractDocumentationProvider.java create mode 100644 src/main/java/de/marhali/easyi18n/assistance/documentation/CommonDocumentationProvider.java create mode 100644 src/main/java/de/marhali/easyi18n/assistance/folding/AbstractFoldingBuilder.java create mode 100644 src/main/java/de/marhali/easyi18n/assistance/folding/JavaFoldingBuilder.java create mode 100644 src/main/java/de/marhali/easyi18n/assistance/folding/KtFoldingBuilder.java create mode 100644 src/main/java/de/marhali/easyi18n/assistance/intention/AbstractExtractIntention.java create mode 100644 src/main/java/de/marhali/easyi18n/assistance/intention/JavaExtractIntention.java create mode 100644 src/main/java/de/marhali/easyi18n/assistance/intention/JsExtractIntention.java create mode 100644 src/main/java/de/marhali/easyi18n/assistance/intention/KtExtractIntention.java create mode 100644 src/main/java/de/marhali/easyi18n/assistance/reference/AbstractKeyReferenceContributor.java create mode 100644 src/main/java/de/marhali/easyi18n/assistance/reference/JavaKeyReferenceContributor.java create mode 100644 src/main/java/de/marhali/easyi18n/assistance/reference/JsKeyReferenceContributor.java create mode 100644 src/main/java/de/marhali/easyi18n/assistance/reference/KtKeyReferenceContributor.java create mode 100644 src/main/java/de/marhali/easyi18n/assistance/reference/PsiKeyReference.java delete mode 100644 src/main/java/de/marhali/easyi18n/editor/KeyAnnotator.java delete mode 100644 src/main/java/de/marhali/easyi18n/editor/KeyCompletionProvider.java delete mode 100644 src/main/java/de/marhali/easyi18n/editor/KeyReference.java delete mode 100644 src/main/java/de/marhali/easyi18n/editor/generic/GenericFoldingBuilder.java delete mode 100644 src/main/java/de/marhali/easyi18n/editor/generic/GenericKeyAnnotator.java delete mode 100644 src/main/java/de/marhali/easyi18n/editor/generic/GenericKeyCompletionContributor.java delete mode 100644 src/main/java/de/marhali/easyi18n/editor/generic/GenericKeyReferenceContributor.java delete mode 100644 src/main/java/de/marhali/easyi18n/editor/kotlin/KotlinKeyAnnotator.java delete mode 100644 src/main/java/de/marhali/easyi18n/editor/kotlin/KotlinKeyReferenceContributor.java diff --git a/src/main/java/de/marhali/easyi18n/assistance/completion/JavaCompletionContributor.java b/src/main/java/de/marhali/easyi18n/assistance/completion/JavaCompletionContributor.java new file mode 100644 index 0000000..8ebc10f --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/assistance/completion/JavaCompletionContributor.java @@ -0,0 +1,16 @@ +package de.marhali.easyi18n.assistance.completion; + +import com.intellij.codeInsight.completion.CompletionContributor; +import com.intellij.codeInsight.completion.CompletionType; +import com.intellij.patterns.PlatformPatterns; +import com.intellij.psi.PsiLiteralValue; + +/** + * Java specific completion contributor + * @author marhali + */ +public class JavaCompletionContributor extends CompletionContributor { + public JavaCompletionContributor() { + extend(CompletionType.BASIC, PlatformPatterns.psiElement().inside(PsiLiteralValue.class), new KeyCompletionProvider()); + } +} diff --git a/src/main/java/de/marhali/easyi18n/assistance/completion/KeyCompletionProvider.java b/src/main/java/de/marhali/easyi18n/assistance/completion/KeyCompletionProvider.java new file mode 100644 index 0000000..eccf9eb --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/assistance/completion/KeyCompletionProvider.java @@ -0,0 +1,60 @@ +package de.marhali.easyi18n.assistance.completion; + +import com.intellij.codeInsight.completion.CompletionParameters; +import com.intellij.codeInsight.completion.CompletionProvider; +import com.intellij.codeInsight.completion.CompletionResultSet; +import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.codeInsight.lookup.LookupElementBuilder; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.IconLoader; +import com.intellij.util.ProcessingContext; + +import de.marhali.easyi18n.InstanceManager; +import de.marhali.easyi18n.assistance.OptionalAssistance; +import de.marhali.easyi18n.model.KeyPath; +import de.marhali.easyi18n.model.Translation; +import de.marhali.easyi18n.model.TranslationData; +import de.marhali.easyi18n.settings.ProjectSettings; +import de.marhali.easyi18n.settings.ProjectSettingsService; +import de.marhali.easyi18n.util.KeyPathConverter; + +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.util.Set; + +/** + * Provides existing translation keys for code completion. + * @author marhali + */ +class KeyCompletionProvider extends CompletionProvider implements OptionalAssistance { + + private static final Icon icon = IconLoader.getIcon("/icons/translate13.svg", KeyCompletionProvider.class); + + @Override + protected void addCompletions(@NotNull CompletionParameters parameters, + @NotNull ProcessingContext context, @NotNull CompletionResultSet result) { + Project project = parameters.getOriginalFile().getProject(); + + if(!isAssistance(project)) { + return; + } + + ProjectSettings settings = ProjectSettingsService.get(project).getState(); + TranslationData data = InstanceManager.get(project).store().getData(); + Set fullKeys = data.getFullKeys(); + + for (KeyPath key : fullKeys) { + result.addElement(constructLookup(new Translation(key, data.getTranslation(key)), settings)); + } + } + + private LookupElement constructLookup(Translation translation, ProjectSettings settings) { + KeyPathConverter converter = new KeyPathConverter(settings); + + return LookupElementBuilder + .create(converter.toString(translation.getKey())) + .withTailText(" " + translation.getValue().get(settings.getPreviewLocale()), true) + .withIcon(icon); + } +} diff --git a/src/main/java/de/marhali/easyi18n/editor/kotlin/KotlinKeyCompletionContributor.java b/src/main/java/de/marhali/easyi18n/assistance/completion/KtCompletionContributor.java similarity index 60% rename from src/main/java/de/marhali/easyi18n/editor/kotlin/KotlinKeyCompletionContributor.java rename to src/main/java/de/marhali/easyi18n/assistance/completion/KtCompletionContributor.java index 30a6a57..eadffb5 100644 --- a/src/main/java/de/marhali/easyi18n/editor/kotlin/KotlinKeyCompletionContributor.java +++ b/src/main/java/de/marhali/easyi18n/assistance/completion/KtCompletionContributor.java @@ -1,19 +1,16 @@ -package de.marhali.easyi18n.editor.kotlin; +package de.marhali.easyi18n.assistance.completion; import com.intellij.codeInsight.completion.CompletionContributor; import com.intellij.codeInsight.completion.CompletionType; import com.intellij.patterns.PlatformPatterns; - -import de.marhali.easyi18n.editor.KeyCompletionProvider; import org.jetbrains.kotlin.psi.KtLiteralStringTemplateEntry; /** - * Kotlin specific translation key completion contributor. + * Kotlin specific completion contributor. * @author marhali */ -public class KotlinKeyCompletionContributor extends CompletionContributor { - - public KotlinKeyCompletionContributor() { +public class KtCompletionContributor extends CompletionContributor { + public KtCompletionContributor() { extend(CompletionType.BASIC, PlatformPatterns.psiElement().inside(KtLiteralStringTemplateEntry.class), new KeyCompletionProvider()); } diff --git a/src/main/java/de/marhali/easyi18n/assistance/documentation/AbstractDocumentationProvider.java b/src/main/java/de/marhali/easyi18n/assistance/documentation/AbstractDocumentationProvider.java new file mode 100644 index 0000000..a2a1d10 --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/assistance/documentation/AbstractDocumentationProvider.java @@ -0,0 +1,104 @@ +package de.marhali.easyi18n.assistance.documentation; + +import com.intellij.lang.documentation.DocumentationMarkup; +import com.intellij.lang.documentation.DocumentationProvider; +import com.intellij.openapi.project.Project; + +import de.marhali.easyi18n.InstanceManager; +import de.marhali.easyi18n.assistance.OptionalAssistance; +import de.marhali.easyi18n.model.KeyPath; +import de.marhali.easyi18n.model.TranslationData; +import de.marhali.easyi18n.model.TranslationNode; +import de.marhali.easyi18n.settings.ProjectSettings; +import de.marhali.easyi18n.settings.ProjectSettingsService; +import de.marhali.easyi18n.util.KeyPathConverter; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.ResourceBundle; + +/** + * Provides locale values as documentation for translation keys. + * @author marhali + */ +abstract class AbstractDocumentationProvider implements DocumentationProvider, OptionalAssistance { + + private static final ResourceBundle bundle = ResourceBundle.getBundle("messages"); + + /** + * Checks if the provided key is a valid translation-key and generates the equivalent documentation for it. + * @param project Opened project + * @param key Designated translation key + * @return Generated documentation or null if not responsible + */ + protected @Nullable String generateDoc(@NotNull Project project, @Nullable String key) { + if(key == null || !isAssistance(project)) { + return null; + } + + ProjectSettings settings = ProjectSettingsService.get(project).getState(); + KeyPathConverter converter = new KeyPathConverter(settings); + KeyPath path = converter.fromString(key); + + // So we want to take care of context and pluralization here + // we should check the last key section for plural / context delims and if so provide all leafs within the last node + + if(path.isEmpty()) { + return null; + } + + TranslationData data = InstanceManager.get(project).store().getData(); + String leaf = path.remove(path.size() - 1); + TranslationNode leafNode = data.getRootNode(); + + for(String section : path) { + leafNode = leafNode.getChildren().get(section); + if(leafNode == null) { // Cannot resolve last node before leaf + return null; + } + } + + Map results = new LinkedHashMap<>(); + + // Filter results for matching leafs (contextual and pluralization support) + for (Map.Entry entry : leafNode.getChildren().entrySet()) { + if(entry.getKey().startsWith(leaf) && entry.getValue().isLeaf()) { + results.put(entry.getKey(), entry.getValue().getValue().get(settings.getPreviewLocale())); + } + } + + if(results.isEmpty()) { // No results to show + return null; + } + + StringBuilder builder = new StringBuilder(); + + builder.append(DocumentationMarkup.DEFINITION_START); + builder.append(bundle.getString("documentation")); + builder.append(DocumentationMarkup.DEFINITION_END); + + if(results.size() == 1) { // Single value + builder.append(DocumentationMarkup.CONTENT_START); + builder.append("").append(results.values().toArray()[0]).append(""); + builder.append(DocumentationMarkup.CONTENT_END); + + } else { // Pluralization | Contextual relevant values + builder.append(DocumentationMarkup.SECTIONS_START); + + for (Map.Entry entry : results.entrySet()) { + builder.append(DocumentationMarkup.SECTION_HEADER_START); + builder.append(entry.getKey()).append(":"); + builder.append(DocumentationMarkup.SECTION_SEPARATOR); + builder.append("

"); + builder.append("").append(entry.getValue()).append(""); + } + + builder.append(DocumentationMarkup.SECTIONS_END); + } + + return builder.toString(); + } +} diff --git a/src/main/java/de/marhali/easyi18n/assistance/documentation/CommonDocumentationProvider.java b/src/main/java/de/marhali/easyi18n/assistance/documentation/CommonDocumentationProvider.java new file mode 100644 index 0000000..5ca0df0 --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/assistance/documentation/CommonDocumentationProvider.java @@ -0,0 +1,26 @@ +package de.marhali.easyi18n.assistance.documentation; + +import com.intellij.psi.PsiElement; +import de.marhali.easyi18n.assistance.reference.PsiKeyReference; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.Nullable; + +/** + * Language unspecific documentation provider. Every supported language should register an extension to this EP. + * @author marhali + */ +public class CommonDocumentationProvider extends AbstractDocumentationProvider { + + @Override + public @Nullable + @Nls String generateDoc(PsiElement element, @Nullable PsiElement originalElement) { + if(!(element instanceof PsiKeyReference.TranslationReference)) { + return null; + } + + PsiKeyReference.TranslationReference keyReference = (PsiKeyReference.TranslationReference) element; + String value = keyReference.getName(); + + return generateDoc(element.getProject(), value); + } +} diff --git a/src/main/java/de/marhali/easyi18n/assistance/folding/AbstractFoldingBuilder.java b/src/main/java/de/marhali/easyi18n/assistance/folding/AbstractFoldingBuilder.java new file mode 100644 index 0000000..5637da7 --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/assistance/folding/AbstractFoldingBuilder.java @@ -0,0 +1,53 @@ +package de.marhali.easyi18n.assistance.folding; + +import com.intellij.lang.ASTNode; +import com.intellij.lang.folding.FoldingBuilderEx; +import com.intellij.openapi.editor.FoldingGroup; +import com.intellij.openapi.project.Project; + +import de.marhali.easyi18n.DataStore; +import de.marhali.easyi18n.InstanceManager; +import de.marhali.easyi18n.assistance.OptionalAssistance; +import de.marhali.easyi18n.model.TranslationValue; +import de.marhali.easyi18n.settings.ProjectSettingsService; +import de.marhali.easyi18n.util.KeyPathConverter; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Language specific folding of translation key with preferred locale value. + * @author marhali + */ +abstract class AbstractFoldingBuilder extends FoldingBuilderEx implements OptionalAssistance { + + protected static final FoldingGroup group = FoldingGroup.newGroup("EasyI18n key folding"); + + /** + * Constructs the folding text for the provided text. + * @param project Opened project + * @param text Designated translation key + * @return Preferred locale value or null if translation does not exists + */ + protected @Nullable String getPlaceholderText(@NotNull Project project, @Nullable String text) { + if(text == null) { + return null; + } + + DataStore store = InstanceManager.get(project).store(); + KeyPathConverter converter = new KeyPathConverter(project); + TranslationValue localeValues = store.getData().getTranslation(converter.fromString(text)); + + if(localeValues == null) { + return null; + } + + String previewLocale = ProjectSettingsService.get(project).getState().getPreviewLocale(); + return localeValues.get(previewLocale); + } + + @Override + public boolean isCollapsedByDefault(@NotNull ASTNode node) { + return true; + } +} diff --git a/src/main/java/de/marhali/easyi18n/assistance/folding/JavaFoldingBuilder.java b/src/main/java/de/marhali/easyi18n/assistance/folding/JavaFoldingBuilder.java new file mode 100644 index 0000000..cac89f3 --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/assistance/folding/JavaFoldingBuilder.java @@ -0,0 +1,66 @@ +package de.marhali.easyi18n.assistance.folding; + +import com.intellij.lang.ASTNode; +import com.intellij.lang.folding.FoldingDescriptor; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiLiteralExpression; +import com.intellij.psi.util.PsiTreeUtil; + +import de.marhali.easyi18n.DataStore; +import de.marhali.easyi18n.InstanceManager; +import de.marhali.easyi18n.util.KeyPathConverter; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * Java specific translation key folding. + * @author marhali + */ +public class JavaFoldingBuilder extends AbstractFoldingBuilder { + + @Override + public FoldingDescriptor @NotNull [] buildFoldRegions(@NotNull PsiElement root, @NotNull Document document, boolean quick) { + List descriptors = new ArrayList<>(); + + if(!isAssistance(root.getProject())) { + return FoldingDescriptor.EMPTY; + } + + Collection literalExpressions = + PsiTreeUtil.findChildrenOfType(root, PsiLiteralExpression.class); + + DataStore store = InstanceManager.get(root.getProject()).store(); + KeyPathConverter converter = new KeyPathConverter(root.getProject()); + + for(final PsiLiteralExpression literalExpression : literalExpressions) { + String value = literalExpression.getValue() instanceof String + ? (String) literalExpression.getValue() : null; + + if(value == null || store.getData().getTranslation(converter.fromString(value)) == null) { + continue; + } + + TextRange range = literalExpression.getTextRange(); + FoldingDescriptor descriptor = new FoldingDescriptor(literalExpression.getNode(), + new TextRange(range.getStartOffset() + 1, range.getEndOffset() - 1), group); + + descriptors.add(descriptor); + } + + return descriptors.toArray(new FoldingDescriptor[0]); + } + + @Override + public @Nullable String getPlaceholderText(@NotNull ASTNode node) { + PsiLiteralExpression literalExpression = node.getPsi(PsiLiteralExpression.class); + String value = literalExpression.getValue() instanceof String ? (String) literalExpression.getValue() : null; + return getPlaceholderText(literalExpression.getProject(), value); + } +} diff --git a/src/main/java/de/marhali/easyi18n/assistance/folding/KtFoldingBuilder.java b/src/main/java/de/marhali/easyi18n/assistance/folding/KtFoldingBuilder.java new file mode 100644 index 0000000..014bfa3 --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/assistance/folding/KtFoldingBuilder.java @@ -0,0 +1,63 @@ +package de.marhali.easyi18n.assistance.folding; + +import com.intellij.lang.ASTNode; +import com.intellij.lang.folding.FoldingDescriptor; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiElement; +import com.intellij.psi.util.PsiTreeUtil; + +import de.marhali.easyi18n.DataStore; +import de.marhali.easyi18n.InstanceManager; +import de.marhali.easyi18n.util.KeyPathConverter; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.kotlin.psi.KtStringTemplateEntry; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * Kotlin specific translation-key folding. + * @author marhali + */ +public class KtFoldingBuilder extends AbstractFoldingBuilder { + @Override + public FoldingDescriptor @NotNull [] buildFoldRegions(@NotNull PsiElement root, @NotNull Document document, boolean quick) { + List descriptors = new ArrayList<>(); + + if(!isAssistance(root.getProject())) { + return FoldingDescriptor.EMPTY; + } + + Collection templateEntries = + PsiTreeUtil.findChildrenOfType(root, KtStringTemplateEntry.class); + + DataStore store = InstanceManager.get(root.getProject()).store(); + KeyPathConverter converter = new KeyPathConverter(root.getProject()); + + for (KtStringTemplateEntry templateEntry : templateEntries) { + String value = templateEntry.getText(); + + if(value == null || store.getData().getTranslation(converter.fromString(value)) == null) { + continue; + } + + TextRange range = templateEntry.getTextRange(); + FoldingDescriptor descriptor = new FoldingDescriptor(templateEntry.getNode(), + new TextRange(range.getStartOffset(), range.getEndOffset()), group); + + descriptors.add(descriptor); + } + + return descriptors.toArray(new FoldingDescriptor[0]); + } + + @Override + public @Nullable String getPlaceholderText(@NotNull ASTNode node) { + KtStringTemplateEntry templateEntry = node.getPsi(KtStringTemplateEntry.class); + return getPlaceholderText(templateEntry.getProject(), templateEntry.getText()); + } +} diff --git a/src/main/java/de/marhali/easyi18n/assistance/intention/AbstractExtractIntention.java b/src/main/java/de/marhali/easyi18n/assistance/intention/AbstractExtractIntention.java new file mode 100644 index 0000000..2f7c2bf --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/assistance/intention/AbstractExtractIntention.java @@ -0,0 +1,93 @@ +package de.marhali.easyi18n.assistance.intention; + +import com.intellij.codeInsight.intention.FileModifier; +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.editor.Caret; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.util.PsiEditorUtil; + +import com.siyeh.ipp.base.MutablyNamedIntention; + +import de.marhali.easyi18n.InstanceManager; +import de.marhali.easyi18n.assistance.OptionalAssistance; +import de.marhali.easyi18n.dialog.AddDialog; +import de.marhali.easyi18n.model.KeyPath; +import de.marhali.easyi18n.model.TranslationData; +import de.marhali.easyi18n.settings.ProjectSettings; +import de.marhali.easyi18n.settings.ProjectSettingsService; +import de.marhali.easyi18n.util.KeyPathConverter; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ResourceBundle; + +/** + * Intention for extracting a translation. Either by translation key or preferred locale value + * @author marhali + */ +abstract class AbstractExtractIntention extends MutablyNamedIntention implements OptionalAssistance { + + protected static final ResourceBundle bundle = ResourceBundle.getBundle("messages"); + + @Override + public @NotNull String getFamilyName() { + return "EasyI18n"; + } + + @Override + public boolean startInWriteAction() { + return false; + } + + protected @NotNull String getTextForElement(@NotNull Project project, @Nullable String key) { + KeyPathConverter converter = new KeyPathConverter(project); + TranslationData data = InstanceManager.get(project).store().getData(); + + return key != null && data.getTranslation(converter.fromString(key)) != null + ? bundle.getString("action.edit") + : bundle.getString("action.extract"); + } + + protected void extractTranslation(@NotNull Project project, @NotNull String text, PsiElement psi) { + ProjectSettings settings = ProjectSettingsService.get(project).getState(); + KeyPathConverter converter = new KeyPathConverter(settings); + + // Extract translation key + // We assume that a text is a translation-key if it contains section delimiters and does not end with them + if(text.contains(settings.getSectionDelimiter()) && !text.endsWith(settings.getSectionDelimiter())) { + new AddDialog(project, converter.fromString(text), null).showAndHandle(); + + } else { // Extract translation value (here preview locale value) + AddDialog dialog = new AddDialog(project, new KeyPath(), text); + + // Replace editor caret with chosen translation key + dialog.registerCallback(translationUpdate -> { + Editor editor = PsiEditorUtil.findEditor(psi); + + if(editor != null) { + Document doc = editor.getDocument(); + Caret caret = editor.getCaretModel().getPrimaryCaret(); + int start = psi.getTextOffset() + 1; + int end = start + text.length(); + + WriteCommandAction.runWriteCommandAction(project, () -> + doc.replaceString(start, end, converter.toString(translationUpdate.getChange().getKey()))); + + caret.removeSelection(); + } + }); + + dialog.showAndHandle();; + } + } + + @Override + public @Nullable FileModifier getFileModifierForPreview(@NotNull PsiFile target) { + return this; + } +} diff --git a/src/main/java/de/marhali/easyi18n/assistance/intention/JavaExtractIntention.java b/src/main/java/de/marhali/easyi18n/assistance/intention/JavaExtractIntention.java new file mode 100644 index 0000000..d074d4f --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/assistance/intention/JavaExtractIntention.java @@ -0,0 +1,35 @@ +package de.marhali.easyi18n.assistance.intention; + +import com.intellij.codeInspection.util.IntentionName; +import com.intellij.psi.*; +import com.siyeh.ipp.base.PsiElementPredicate; +import org.jetbrains.annotations.NotNull; + +/** + * Java specific translation key intention. + * @author marhali + */ +public class JavaExtractIntention extends AbstractExtractIntention { + + @Override + protected @IntentionName String getTextForElement(PsiElement element) { + return getTextForElement(element.getProject(), (String) ((PsiLiteralExpression) element).getValue()); + } + + @Override + protected void processIntention(@NotNull PsiElement element) { + System.out.println("proci2"); + if(!(element instanceof PsiLiteralExpression) + || !(((PsiLiteralExpression) element).getValue() instanceof String)) { + return; + } + + extractTranslation(element.getProject(), (String) ((PsiLiteralExpression) element).getValue(), element); + } + + @Override + protected @NotNull PsiElementPredicate getElementPredicate() { + System.out.println("predi2"); + return element -> element instanceof PsiLiteralExpression; + } +} diff --git a/src/main/java/de/marhali/easyi18n/assistance/intention/JsExtractIntention.java b/src/main/java/de/marhali/easyi18n/assistance/intention/JsExtractIntention.java new file mode 100644 index 0000000..5496802 --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/assistance/intention/JsExtractIntention.java @@ -0,0 +1,40 @@ +package de.marhali.easyi18n.assistance.intention; + +import com.intellij.codeInspection.util.IntentionName; +import com.intellij.lang.javascript.psi.JSLiteralExpression; +import com.intellij.psi.PsiElement; +import com.siyeh.ipp.base.PsiElementPredicate; +import org.jetbrains.annotations.NotNull; + +/** + * JavaScript specific translation key intention. + * @author marhali + */ +// TODO: current implementation does not support other languages than Java +public class JsExtractIntention extends AbstractExtractIntention { + + @Override + public @NotNull String getFamilyName() { + return "JavaScript"; + } + + @Override + protected @IntentionName String getTextForElement(PsiElement element) { + return "HILFE"; + //return getTextForElement(element.getProject(), ((JSLiteralExpression) element).getStringValue()); + } + + @Override + protected void processIntention(@NotNull PsiElement element) { + if(!(element instanceof JSLiteralExpression) || ((JSLiteralExpression) element).getStringValue() == null) { + return; + } + + extractTranslation(element.getProject(), ((JSLiteralExpression) element).getStringValue(), element); + } + + @Override + protected @NotNull PsiElementPredicate getElementPredicate() { + return element -> true; + } +} diff --git a/src/main/java/de/marhali/easyi18n/assistance/intention/KtExtractIntention.java b/src/main/java/de/marhali/easyi18n/assistance/intention/KtExtractIntention.java new file mode 100644 index 0000000..bdaf628 --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/assistance/intention/KtExtractIntention.java @@ -0,0 +1,41 @@ +package de.marhali.easyi18n.assistance.intention; + +import com.intellij.codeInspection.util.IntentionName; +import com.intellij.psi.PsiElement; +import com.siyeh.ipp.base.PsiElementPredicate; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.kotlin.psi.KtLiteralStringTemplateEntry; +import org.jetbrains.kotlin.psi.KtStringTemplateExpression; + +/** + * Kotlin specific translation key intention. + * @author marhali + */ +// TODO: kotlin impl does not work - no action +public class KtExtractIntention extends AbstractExtractIntention { + @Override + protected @IntentionName String getTextForElement(PsiElement element) { + return "hallo"; + //return getTextForElement(element.getProject(), element.getText()); + } + + @Override + protected void processIntention(@NotNull PsiElement element) { + System.out.println("Hallo"); + + if(!(element instanceof KtStringTemplateExpression)) { + System.out.println(element.getClass()); + return; + } + + System.out.println("hallo"); + + extractTranslation(element.getProject() , element.getText(), element); + } + + @Override + protected @NotNull PsiElementPredicate getElementPredicate() { + System.out.println("predi"); + return element -> element instanceof KtLiteralStringTemplateEntry; + } +} diff --git a/src/main/java/de/marhali/easyi18n/assistance/reference/AbstractKeyReferenceContributor.java b/src/main/java/de/marhali/easyi18n/assistance/reference/AbstractKeyReferenceContributor.java new file mode 100644 index 0000000..b964a4c --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/assistance/reference/AbstractKeyReferenceContributor.java @@ -0,0 +1,53 @@ +package de.marhali.easyi18n.assistance.reference; + +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiReference; +import com.intellij.psi.PsiReferenceContributor; +import de.marhali.easyi18n.InstanceManager; +import de.marhali.easyi18n.assistance.OptionalAssistance; +import de.marhali.easyi18n.model.KeyPath; +import de.marhali.easyi18n.model.Translation; +import de.marhali.easyi18n.model.TranslationValue; +import de.marhali.easyi18n.settings.ProjectSettings; +import de.marhali.easyi18n.settings.ProjectSettingsService; +import de.marhali.easyi18n.util.KeyPathConverter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Language specific translation key reference contributor. + * @author marhali + */ +abstract class AbstractKeyReferenceContributor extends PsiReferenceContributor implements OptionalAssistance { + /** + * Searches for relevant translation-key references + * @param project Opened project + * @param element Targeted element + * @param text Designated translation key + * @return Matched translation-key reference(s) + */ + protected @NotNull PsiReference[] getReferences( + @NotNull Project project, @NotNull PsiElement element, @Nullable String text) { + + if(text == null || text.isEmpty() || !isAssistance(project)) { + return PsiReference.EMPTY_ARRAY; + } + + ProjectSettings settings = ProjectSettingsService.get(project).getState(); + KeyPathConverter converter = new KeyPathConverter(settings); + + // TODO: We should provide multiple references if not a leaf node was provided (contextual / plurals support) + + KeyPath path = converter.fromString(text); + TranslationValue values = InstanceManager.get(project).store().getData().getTranslation(path); + + if(values == null) { // We only reference valid and existing translations + return PsiReference.EMPTY_ARRAY; + } + + return new PsiReference[] { + new PsiKeyReference(converter, new Translation(path, values), element) + }; + } +} diff --git a/src/main/java/de/marhali/easyi18n/assistance/reference/JavaKeyReferenceContributor.java b/src/main/java/de/marhali/easyi18n/assistance/reference/JavaKeyReferenceContributor.java new file mode 100644 index 0000000..bff2307 --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/assistance/reference/JavaKeyReferenceContributor.java @@ -0,0 +1,40 @@ +package de.marhali.easyi18n.assistance.reference; + +import com.intellij.openapi.project.Project; +import com.intellij.patterns.PlatformPatterns; +import com.intellij.psi.*; + +import com.intellij.util.ProcessingContext; + +import org.jetbrains.annotations.NotNull; + +/** + * Java specific key reference binding. + * @author marhali + */ +public class JavaKeyReferenceContributor extends AbstractKeyReferenceContributor { + + // TODO: why not PsiLiteralExpression? + + @Override + public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) { + registrar.registerReferenceProvider( + PlatformPatterns.psiElement(PsiLiteralValue.class), + getProvider()); + } + + private PsiReferenceProvider getProvider() { + return new PsiReferenceProvider() { + @Override + public PsiReference @NotNull [] getReferencesByElement( + @NotNull PsiElement element, @NotNull ProcessingContext context) { + + Project project = element.getProject(); + PsiLiteralValue literalValue = (PsiLiteralValue) element; + String value = literalValue.getValue() instanceof String ? (String) literalValue.getValue() : null; + + return getReferences(project, element, value); + } + }; + } +} diff --git a/src/main/java/de/marhali/easyi18n/assistance/reference/JsKeyReferenceContributor.java b/src/main/java/de/marhali/easyi18n/assistance/reference/JsKeyReferenceContributor.java new file mode 100644 index 0000000..6edd656 --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/assistance/reference/JsKeyReferenceContributor.java @@ -0,0 +1,39 @@ +package de.marhali.easyi18n.assistance.reference; + +import com.intellij.lang.javascript.psi.JSLiteralExpression; +import com.intellij.openapi.project.Project; +import com.intellij.patterns.PlatformPatterns; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiReference; +import com.intellij.psi.PsiReferenceProvider; +import com.intellij.psi.PsiReferenceRegistrar; +import com.intellij.util.ProcessingContext; +import org.jetbrains.annotations.NotNull; + +/** + * JavaScript specific translation-key reference binding. + * @author marhali + */ +public class JsKeyReferenceContributor extends AbstractKeyReferenceContributor { + @Override + public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) { + registrar.registerReferenceProvider( + PlatformPatterns.psiElement(JSLiteralExpression.class), + getProvider()); + } + + private PsiReferenceProvider getProvider() { + return new PsiReferenceProvider() { + @Override + public PsiReference @NotNull [] getReferencesByElement( + @NotNull PsiElement element, @NotNull ProcessingContext context) { + + Project project = element.getProject(); + JSLiteralExpression literalExpression = (JSLiteralExpression) element; + String value = literalExpression.getStringValue(); + + return getReferences(project, element, value); + } + }; + } +} \ No newline at end of file diff --git a/src/main/java/de/marhali/easyi18n/assistance/reference/KtKeyReferenceContributor.java b/src/main/java/de/marhali/easyi18n/assistance/reference/KtKeyReferenceContributor.java new file mode 100644 index 0000000..dc1b0e1 --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/assistance/reference/KtKeyReferenceContributor.java @@ -0,0 +1,46 @@ +package de.marhali.easyi18n.assistance.reference; + +import com.intellij.openapi.project.Project; +import com.intellij.patterns.PlatformPatterns; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiReference; +import com.intellij.psi.PsiReferenceProvider; +import com.intellij.psi.PsiReferenceRegistrar; +import com.intellij.util.ProcessingContext; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.kotlin.psi.KtLiteralStringTemplateEntry; +import org.jetbrains.kotlin.psi.KtStringTemplateExpression; + +import java.util.Arrays; +import java.util.Optional; + +/** + * Kotlin specific translation-key reference binding. + * @author marhali + */ +public class KtKeyReferenceContributor extends AbstractKeyReferenceContributor { + @Override + public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) { + registrar.registerReferenceProvider( + PlatformPatterns.psiElement().inside(KtStringTemplateExpression.class), + getProvider()); + } + + private PsiReferenceProvider getProvider() { + return new PsiReferenceProvider() { + @Override + public PsiReference @NotNull [] getReferencesByElement( + @NotNull PsiElement element, @NotNull ProcessingContext context) { + + Optional targetElement = Arrays.stream(element.getChildren()).filter(child -> + child instanceof KtLiteralStringTemplateEntry).findAny(); + + if(targetElement.isEmpty()) { + return PsiReference.EMPTY_ARRAY; + } + + return getReferences(element.getProject(), element, targetElement.get().getText()); + } + }; + } +} diff --git a/src/main/java/de/marhali/easyi18n/assistance/reference/PsiKeyReference.java b/src/main/java/de/marhali/easyi18n/assistance/reference/PsiKeyReference.java new file mode 100644 index 0000000..58a45e7 --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/assistance/reference/PsiKeyReference.java @@ -0,0 +1,72 @@ +package de.marhali.easyi18n.assistance.reference; + +import com.intellij.navigation.ItemPresentation; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiReferenceBase; +import com.intellij.psi.SyntheticElement; +import com.intellij.psi.impl.FakePsiElement; + +import de.marhali.easyi18n.dialog.AddDialog; +import de.marhali.easyi18n.dialog.EditDialog; +import de.marhali.easyi18n.model.Translation; +import de.marhali.easyi18n.util.KeyPathConverter; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * References translation keys inside editor with corresponding {@link EditDialog} / {@link AddDialog}. + * @author marhali + */ +public class PsiKeyReference extends PsiReferenceBase { + + private final @NotNull Translation translation; + private final @NotNull KeyPathConverter converter; + + protected PsiKeyReference( + @NotNull KeyPathConverter converter, @NotNull Translation translation, @NotNull PsiElement element) { + + super(element, true); + this.translation = translation; + this.converter = converter; + } + + public @NotNull String getKey() { + return converter.toString(translation.getKey()); + } + + @Override + public @Nullable PsiElement resolve() { + return new TranslationReference(); + } + + public class TranslationReference extends FakePsiElement implements SyntheticElement { + @Override + public PsiElement getParent() { + return myElement; + } + + @Override + public void navigate(boolean requestFocus) { + new EditDialog(getProject(), translation).showAndHandle(); + } + + @Override + public String getPresentableText() { + return getKey(); + } + + @Override + public String getName() { + return getKey(); + } + + @Override + public @Nullable TextRange getTextRange() { + TextRange rangeInElement = getRangeInElement(); + TextRange elementRange = myElement.getTextRange(); + return elementRange != null ? rangeInElement.shiftRight(elementRange.getStartOffset()) : rangeInElement; + } + } +} \ No newline at end of file diff --git a/src/main/java/de/marhali/easyi18n/editor/KeyAnnotator.java b/src/main/java/de/marhali/easyi18n/editor/KeyAnnotator.java deleted file mode 100644 index aa3139a..0000000 --- a/src/main/java/de/marhali/easyi18n/editor/KeyAnnotator.java +++ /dev/null @@ -1,61 +0,0 @@ -package de.marhali.easyi18n.editor; - -import com.intellij.lang.annotation.AnnotationHolder; -import com.intellij.lang.annotation.HighlightSeverity; -import com.intellij.openapi.project.Project; - -import de.marhali.easyi18n.InstanceManager; -import de.marhali.easyi18n.model.KeyPath; -import de.marhali.easyi18n.model.KeyPathConverter; -import de.marhali.easyi18n.model.TranslationNode; - -import de.marhali.easyi18n.settings.ProjectSettings; -import de.marhali.easyi18n.settings.ProjectSettingsService; -import org.jetbrains.annotations.NotNull; - -/** - * Superclass for managing key annotations. - * @author marhali - */ -public class KeyAnnotator { - - /** - * Adds annotations for i18n keys with content preview for preferred locale. - * @param key I18n key extracted by psi element - * @param project Project instance - * @param holder Annotation holder - */ - protected void annotate(@NotNull String key, @NotNull Project project, @NotNull AnnotationHolder holder) { - // Do not annotate keys if service is disabled - if(!ProjectSettingsService.get(project).getState().isAssistance()) { - return; - } - - ProjectSettings state = ProjectSettingsService.get(project).getState(); - //String pathPrefix = state.getPathPrefix(); - // TODO: Path prefix removal - String pathPrefix = ""; - String previewLocale = state.getPreviewLocale(); - - KeyPathConverter converter = new KeyPathConverter(project); - - String searchKey = key.length() >= pathPrefix.length() - ? key.substring(pathPrefix.length()) - : key; - - if(searchKey.startsWith(KeyPath.DELIMITER)) { - searchKey = searchKey.substring(KeyPath.DELIMITER.length()); - } - - TranslationNode node = InstanceManager.get(project).store().getData().getNode(converter.split(searchKey)); - - if(node == null) { // Unknown translation. Just ignore it - return; - } - - String tooltip = node.isLeaf() ? "I18n(" + previewLocale + ": " + node.getValue().get(previewLocale) + ")" - : "I18n ([])"; - - holder.newAnnotation(HighlightSeverity.INFORMATION, tooltip).create(); - } -} \ No newline at end of file diff --git a/src/main/java/de/marhali/easyi18n/editor/KeyCompletionProvider.java b/src/main/java/de/marhali/easyi18n/editor/KeyCompletionProvider.java deleted file mode 100644 index ac899d2..0000000 --- a/src/main/java/de/marhali/easyi18n/editor/KeyCompletionProvider.java +++ /dev/null @@ -1,64 +0,0 @@ -package de.marhali.easyi18n.editor; - -import com.intellij.codeInsight.completion.*; -import com.intellij.codeInsight.lookup.*; -import com.intellij.icons.AllIcons; -import com.intellij.openapi.project.*; -import com.intellij.util.*; - -import de.marhali.easyi18n.DataStore; -import de.marhali.easyi18n.InstanceManager; -import de.marhali.easyi18n.model.KeyPath; -import de.marhali.easyi18n.model.Translation; - -import de.marhali.easyi18n.settings.ProjectSettingsService; -import org.jetbrains.annotations.*; - -import java.util.*; - -/** - * I18n translation key completion provider. - * @author marhali - */ -public class KeyCompletionProvider extends CompletionProvider { - - @Override - protected void addCompletions(@NotNull CompletionParameters parameters, - @NotNull ProcessingContext context, @NotNull CompletionResultSet result) { - - Project project = parameters.getOriginalFile().getProject(); - - // Do not annotate keys if service is disabled - if(!ProjectSettingsService.get(project).getState().isAssistance()) { - return; - } - - DataStore store = InstanceManager.get(project).store(); - - String previewLocale = ProjectSettingsService.get(project).getState().getPreviewLocale(); - //String pathPrefix = ProjectSettingsService.get(project).getState().getPathPrefix(); - // TODO: Path prefix removal - String pathPrefix = ""; - - if(pathPrefix.length() > 0 && !pathPrefix.endsWith(KeyPath.DELIMITER)) { - pathPrefix += KeyPath.DELIMITER; - } - - Set fullKeys = store.getData().getFullKeys(); - - for(KeyPath currentKey : fullKeys) { - result.addElement(createElement( - pathPrefix, - currentKey, - previewLocale, - Objects.requireNonNull(store.getData().getTranslation(currentKey)) - )); - } - } - - private LookupElement createElement(String prefix, KeyPath path, String locale, Translation translation) { - return LookupElementBuilder.create(prefix + path.toSimpleString()) - .withIcon(AllIcons.Actions.PreserveCaseHover) - .appendTailText(" I18n(" + locale + ": " + translation.get(locale) + ")", true); - } -} \ No newline at end of file diff --git a/src/main/java/de/marhali/easyi18n/editor/KeyReference.java b/src/main/java/de/marhali/easyi18n/editor/KeyReference.java deleted file mode 100644 index d848bc1..0000000 --- a/src/main/java/de/marhali/easyi18n/editor/KeyReference.java +++ /dev/null @@ -1,93 +0,0 @@ -package de.marhali.easyi18n.editor; - -import com.intellij.openapi.util.TextRange; -import com.intellij.psi.*; -import com.intellij.psi.impl.FakePsiElement; - -import de.marhali.easyi18n.InstanceManager; -import de.marhali.easyi18n.dialog.AddDialog; -import de.marhali.easyi18n.dialog.EditDialog; -import de.marhali.easyi18n.model.KeyPath; -import de.marhali.easyi18n.model.KeyPathConverter; -import de.marhali.easyi18n.model.KeyedTranslation; -import de.marhali.easyi18n.model.Translation; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * Go to declaration reference for i18n keys. - * @author marhali - */ -public class KeyReference extends PsiReferenceBase { - - @Nullable private final String myKey; - - public KeyReference(@NotNull final PsiElement element) { - this(element, (String)null); - } - - public KeyReference(@NotNull final PsiElement element, @Nullable final String myKey) { - super(element, true); - this.myKey = myKey; - } - - public KeyReference(@NotNull final PsiElement element, @NotNull TextRange textRange) { - this(element, textRange, null); - } - - public KeyReference(@NotNull PsiElement element, TextRange textRange, @Nullable String myKey) { - super(element, textRange, true); - this.myKey = myKey; - } - - @Override - public @Nullable PsiElement resolve() { - return new TranslationKey(); - } - - public String getKey() { - return myKey != null ? myKey : getValue(); - } - - class TranslationKey extends FakePsiElement implements SyntheticElement { - @Override - public PsiElement getParent() { - return myElement; - } - - @Override - public void navigate(boolean requestFocus) { - KeyPathConverter converter = new KeyPathConverter(getProject()); - KeyPath path = converter.split(getKey()); - Translation translation = InstanceManager.get(getProject()).store().getData().getTranslation(path); - - if(translation != null) { - new EditDialog(getProject(), new KeyedTranslation(path, translation)).showAndHandle(); - } else { - new AddDialog(getProject(), path).showAndHandle(); - } - } - - @Override - public String getPresentableText() { - return getKey(); - } - - @Override - public String getName() { - return getKey(); - } - - @Override - public @Nullable TextRange getTextRange() { - final TextRange rangeInElement = getRangeInElement(); - final TextRange elementRange = myElement.getTextRange(); - return elementRange != null ? rangeInElement.shiftRight(elementRange.getStartOffset()) : rangeInElement; - } - } - - public static boolean isReferencable(String value) { - return value.matches("^[^\\s:/]+$"); - } -} \ No newline at end of file diff --git a/src/main/java/de/marhali/easyi18n/editor/generic/GenericFoldingBuilder.java b/src/main/java/de/marhali/easyi18n/editor/generic/GenericFoldingBuilder.java deleted file mode 100644 index a54310b..0000000 --- a/src/main/java/de/marhali/easyi18n/editor/generic/GenericFoldingBuilder.java +++ /dev/null @@ -1,87 +0,0 @@ -package de.marhali.easyi18n.editor.generic; - -import com.intellij.lang.ASTNode; -import com.intellij.lang.folding.FoldingBuilderEx; -import com.intellij.lang.folding.FoldingDescriptor; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.util.TextRange; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiLiteralValue; -import com.intellij.psi.util.PsiTreeUtil; - -import de.marhali.easyi18n.DataStore; -import de.marhali.easyi18n.InstanceManager; -import de.marhali.easyi18n.model.KeyPathConverter; -import de.marhali.easyi18n.model.Translation; -import de.marhali.easyi18n.settings.ProjectSettingsService; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -/** - * Translation key folding with actual value based on i18n instance. - * @author marhali - */ -public class GenericFoldingBuilder extends FoldingBuilderEx { - - @Override - public FoldingDescriptor @NotNull [] buildFoldRegions(@NotNull PsiElement root, @NotNull Document document, boolean quick) { - Collection literalValues = PsiTreeUtil.findChildrenOfType(root, PsiLiteralValue.class); - List descriptors = new ArrayList<>(); - - if(!ProjectSettingsService.get(root.getProject()).getState().isAssistance()) { - return FoldingDescriptor.EMPTY; - } - - DataStore store = InstanceManager.get(root.getProject()).store(); - KeyPathConverter converter = new KeyPathConverter(root.getProject()); - - for(final PsiLiteralValue literalValue : literalValues) { - String value = literalValue.getValue() instanceof String ? (String) literalValue.getValue() : null; - - // Undefined string literal or not a translation - if(value == null || store.getData().getTranslation(converter.split(value)) == null) { - continue; - } - - descriptors.add(new FoldingDescriptor(literalValue.getNode(), - new TextRange(literalValue.getTextRange().getStartOffset() + 1, - literalValue.getTextRange().getEndOffset() - 1))); - } - - return descriptors.toArray(new FoldingDescriptor[0]); - } - - @Nullable - @Override - public String getPlaceholderText(@NotNull ASTNode node) { - PsiLiteralValue literalValue = node.getPsi(PsiLiteralValue.class); - String value = literalValue.getValue() instanceof String ? (String) literalValue.getValue() : null; - - if(value == null) { - return null; - } - - DataStore store = InstanceManager.get(literalValue.getProject()).store(); - KeyPathConverter converter = new KeyPathConverter(literalValue.getProject()); - - Translation translation = store.getData().getTranslation(converter.split(value)); - - if(translation == null) { - return null; - } - - String previewLocale = ProjectSettingsService.get(literalValue.getProject()).getState().getPreviewLocale(); - - return translation.get(previewLocale); - } - - @Override - public boolean isCollapsedByDefault(@NotNull ASTNode node) { - return true; - } -} \ No newline at end of file diff --git a/src/main/java/de/marhali/easyi18n/editor/generic/GenericKeyAnnotator.java b/src/main/java/de/marhali/easyi18n/editor/generic/GenericKeyAnnotator.java deleted file mode 100644 index 8de8149..0000000 --- a/src/main/java/de/marhali/easyi18n/editor/generic/GenericKeyAnnotator.java +++ /dev/null @@ -1,33 +0,0 @@ -package de.marhali.easyi18n.editor.generic; - -import com.intellij.lang.annotation.AnnotationHolder; -import com.intellij.lang.annotation.Annotator; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiLiteralValue; - -import de.marhali.easyi18n.editor.KeyAnnotator; - -import org.jetbrains.annotations.NotNull; - -/** - * Translation key annotator for generic languages which support {@link PsiLiteralValue}. - * @author marhali - */ -public class GenericKeyAnnotator extends KeyAnnotator implements Annotator { - - @Override - public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder holder) { - if(!(element instanceof PsiLiteralValue)) { - return; - } - - PsiLiteralValue literalValue = (PsiLiteralValue) element; - String value = literalValue.getValue() instanceof String ? (String) literalValue.getValue() : null; - - if(value == null) { - return; - } - - annotate(value, element.getProject(), holder); - } -} \ No newline at end of file diff --git a/src/main/java/de/marhali/easyi18n/editor/generic/GenericKeyCompletionContributor.java b/src/main/java/de/marhali/easyi18n/editor/generic/GenericKeyCompletionContributor.java deleted file mode 100644 index a8ed832..0000000 --- a/src/main/java/de/marhali/easyi18n/editor/generic/GenericKeyCompletionContributor.java +++ /dev/null @@ -1,24 +0,0 @@ -package de.marhali.easyi18n.editor.generic; - -import com.intellij.codeInsight.completion.CompletionContributor; -import com.intellij.codeInsight.completion.CompletionType; -import com.intellij.patterns.*; -import com.intellij.psi.*; -import com.intellij.psi.xml.*; -import de.marhali.easyi18n.editor.KeyCompletionProvider; - -/** - * Translation key completion for generic languages which support {@link PsiLiteralValue}. - * @author marhali - */ -public class GenericKeyCompletionContributor extends CompletionContributor { - - public GenericKeyCompletionContributor() { - extend(CompletionType.BASIC, PlatformPatterns.psiElement(PlainTextTokenTypes.PLAIN_TEXT), - new KeyCompletionProvider()); - extend(CompletionType.BASIC, PlatformPatterns.psiElement().inside(XmlElement.class), - new KeyCompletionProvider()); - extend(CompletionType.BASIC, PlatformPatterns.psiElement().inside(PsiLiteralValue.class), - new KeyCompletionProvider()); - } -} \ No newline at end of file diff --git a/src/main/java/de/marhali/easyi18n/editor/generic/GenericKeyReferenceContributor.java b/src/main/java/de/marhali/easyi18n/editor/generic/GenericKeyReferenceContributor.java deleted file mode 100644 index 9d05418..0000000 --- a/src/main/java/de/marhali/easyi18n/editor/generic/GenericKeyReferenceContributor.java +++ /dev/null @@ -1,54 +0,0 @@ -package de.marhali.easyi18n.editor.generic; - -import com.intellij.patterns.PlatformPatterns; -import com.intellij.psi.*; -import com.intellij.util.ProcessingContext; - -import de.marhali.easyi18n.InstanceManager; -import de.marhali.easyi18n.editor.KeyReference; -import de.marhali.easyi18n.model.KeyPathConverter; - -import de.marhali.easyi18n.settings.ProjectSettingsService; -import org.jetbrains.annotations.NotNull; - -/** - * Generic translation key reference contributor. - * @author marhali - */ -public class GenericKeyReferenceContributor extends PsiReferenceContributor { - @Override - public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) { - registrar.registerReferenceProvider(PlatformPatterns.psiElement(PsiLiteralValue.class), getProvider()); - } - - private PsiReferenceProvider getProvider() { - return new PsiReferenceProvider() { - @Override - public PsiReference @NotNull [] getReferencesByElement( - @NotNull PsiElement element, @NotNull ProcessingContext context) { - - PsiLiteralValue literalValue = (PsiLiteralValue) element; - String value = literalValue.getValue() instanceof String ? (String) literalValue.getValue() : null; - - if(value == null) { - return PsiReference.EMPTY_ARRAY; - } - - // Do not reference keys if service is disabled - if(!ProjectSettingsService.get(element.getProject()).getState().isAssistance()) { - return PsiReference.EMPTY_ARRAY; - } - - KeyPathConverter converter = new KeyPathConverter(element.getProject()); - - if(InstanceManager.get(element.getProject()).store().getData().getTranslation(converter.split(value)) == null) { - if(!KeyReference.isReferencable(value)) { // Creation policy - return PsiReference.EMPTY_ARRAY; - } - } - - return new PsiReference[] { new KeyReference(element, value) }; - } - }; - } -} diff --git a/src/main/java/de/marhali/easyi18n/editor/kotlin/KotlinKeyAnnotator.java b/src/main/java/de/marhali/easyi18n/editor/kotlin/KotlinKeyAnnotator.java deleted file mode 100644 index 4e9cfe3..0000000 --- a/src/main/java/de/marhali/easyi18n/editor/kotlin/KotlinKeyAnnotator.java +++ /dev/null @@ -1,31 +0,0 @@ -package de.marhali.easyi18n.editor.kotlin; - -import com.intellij.lang.annotation.AnnotationHolder; -import com.intellij.lang.annotation.Annotator; -import com.intellij.psi.PsiElement; - -import de.marhali.easyi18n.editor.KeyAnnotator; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.kotlin.psi.KtLiteralStringTemplateEntry; - -/** - * Kotlin specific translation key annotator - * @author marhali - */ -public class KotlinKeyAnnotator extends KeyAnnotator implements Annotator { - - @Override - public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder holder) { - if(!(element instanceof KtLiteralStringTemplateEntry)) { - return; - } - - String value = element.getText(); - - if(value == null) { - return; - } - - annotate(value, element.getProject(), holder); - } -} \ No newline at end of file diff --git a/src/main/java/de/marhali/easyi18n/editor/kotlin/KotlinKeyReferenceContributor.java b/src/main/java/de/marhali/easyi18n/editor/kotlin/KotlinKeyReferenceContributor.java deleted file mode 100644 index fe37717..0000000 --- a/src/main/java/de/marhali/easyi18n/editor/kotlin/KotlinKeyReferenceContributor.java +++ /dev/null @@ -1,59 +0,0 @@ -package de.marhali.easyi18n.editor.kotlin; - -import com.intellij.patterns.PlatformPatterns; -import com.intellij.psi.*; - -import com.intellij.util.ProcessingContext; - -import de.marhali.easyi18n.InstanceManager; -import de.marhali.easyi18n.editor.KeyReference; -import de.marhali.easyi18n.model.KeyPathConverter; - -import de.marhali.easyi18n.settings.ProjectSettingsService; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.kotlin.psi.KtLiteralStringTemplateEntry; -import org.jetbrains.kotlin.psi.KtStringTemplateExpression; - -/** - * Kotlin translation key reference contributor. - * @author marhali - */ -public class KotlinKeyReferenceContributor extends PsiReferenceContributor { - - @Override - public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) { - registrar.registerReferenceProvider(PlatformPatterns.psiElement().inside(KtStringTemplateExpression.class), getProvider()); - } - - private PsiReferenceProvider getProvider() { - return new PsiReferenceProvider() { - @Override - public PsiReference @NotNull [] getReferencesByElement(@NotNull PsiElement element, @NotNull ProcessingContext context) { - String value = null; - - for (PsiElement child : element.getChildren()) { - if(child instanceof KtLiteralStringTemplateEntry) { - value = child.getText(); - } - } - - if(value == null) { - return PsiReference.EMPTY_ARRAY; - } - - // Do not reference keys if service is disabled - if(!ProjectSettingsService.get(element.getProject()).getState().isAssistance()) { - return PsiReference.EMPTY_ARRAY; - } - - KeyPathConverter converter = new KeyPathConverter(element.getProject()); - - if(InstanceManager.get(element.getProject()).store().getData().getNode(converter.split(value)) == null) { - return PsiReference.EMPTY_ARRAY; - } - - return new PsiReference[] { new KeyReference(element, value) }; - } - }; - } -} diff --git a/src/main/resources/META-INF/de.marhali.easyi18n-java.xml b/src/main/resources/META-INF/de.marhali.easyi18n-java.xml index 4bf69d7..43a9382 100644 --- a/src/main/resources/META-INF/de.marhali.easyi18n-java.xml +++ b/src/main/resources/META-INF/de.marhali.easyi18n-java.xml @@ -1,5 +1,27 @@ - + + de.marhali.easyi18n.assistance.intention.JavaExtractIntention + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/META-INF/de.marhali.easyi18n-javascript.xml b/src/main/resources/META-INF/de.marhali.easyi18n-javascript.xml index 132273b..2c12e83 100644 --- a/src/main/resources/META-INF/de.marhali.easyi18n-javascript.xml +++ b/src/main/resources/META-INF/de.marhali.easyi18n-javascript.xml @@ -1,6 +1,17 @@ - - + + + + + + de.marhali.easyi18n.assistance.intention.JsExtractIntention + \ No newline at end of file diff --git a/src/main/resources/META-INF/de.marhali.easyi18n-kotlin.xml b/src/main/resources/META-INF/de.marhali.easyi18n-kotlin.xml index fe81e94..b1b898b 100644 --- a/src/main/resources/META-INF/de.marhali.easyi18n-kotlin.xml +++ b/src/main/resources/META-INF/de.marhali.easyi18n-kotlin.xml @@ -1,11 +1,27 @@ - + + de.marhali.easyi18n.assistance.intention.KtExtractIntention + - + - + + + + + \ No newline at end of file diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 8df51ed..b1274f6 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -34,15 +34,6 @@ id="de.marhali.easyi18n.service.AppSettingsConfigurable" displayName="Easy I18n" nonDefaultProject="true"/> - - - - - - From fc107aa8396f89af23ed6f1f14f1e8a08d59b531 Mon Sep 17 00:00:00 2001 From: marhali Date: Thu, 21 Apr 2022 20:06:28 +0200 Subject: [PATCH 45/51] finalize editor assistance --- CHANGELOG.md | 1 + gradle.properties | 2 +- .../completion/JavaCompletionContributor.java | 7 +- .../completion/JsCompletionContributor.java | 17 +++ .../completion/PhpCompletionContributor.java | 17 +++ .../folding/AbstractFoldingBuilder.java | 65 +++++++-- .../folding/JavaFoldingBuilder.java | 50 +------ .../assistance/folding/JsFoldingBuilder.java | 32 ++++ .../assistance/folding/KtFoldingBuilder.java | 52 +++---- .../assistance/folding/PhpFoldingBuilder.java | 31 ++++ .../intention/AbstractExtractIntention.java | 93 ------------ .../AbstractTranslationIntention.java | 138 ++++++++++++++++++ .../intention/JavaExtractIntention.java | 35 ----- .../intention/JavaTranslationIntention.java | 27 ++++ .../intention/JsExtractIntention.java | 40 ----- .../intention/JsTranslationIntention.java | 28 ++++ .../intention/KtExtractIntention.java | 41 ------ .../intention/KtTranslationIntention.java | 23 +++ .../intention/PhpTranslationIntention.java | 23 +++ .../JavaKeyReferenceContributor.java | 11 +- .../reference/PhpKeyReferenceContributor.java | 37 +++++ .../META-INF/de.marhali.easyi18n-java.xml | 2 +- .../de.marhali.easyi18n-javascript.xml | 30 +++- .../META-INF/de.marhali.easyi18n-kotlin.xml | 2 +- .../META-INF/de.marhali.easyi18n-php.xml | 27 ++++ .../META-INF/de.marhali.easyi18n-vue.xml | 13 +- src/main/resources/META-INF/plugin.xml | 1 + 27 files changed, 536 insertions(+), 309 deletions(-) create mode 100644 src/main/java/de/marhali/easyi18n/assistance/completion/JsCompletionContributor.java create mode 100644 src/main/java/de/marhali/easyi18n/assistance/completion/PhpCompletionContributor.java create mode 100644 src/main/java/de/marhali/easyi18n/assistance/folding/JsFoldingBuilder.java create mode 100644 src/main/java/de/marhali/easyi18n/assistance/folding/PhpFoldingBuilder.java delete mode 100644 src/main/java/de/marhali/easyi18n/assistance/intention/AbstractExtractIntention.java create mode 100644 src/main/java/de/marhali/easyi18n/assistance/intention/AbstractTranslationIntention.java delete mode 100644 src/main/java/de/marhali/easyi18n/assistance/intention/JavaExtractIntention.java create mode 100644 src/main/java/de/marhali/easyi18n/assistance/intention/JavaTranslationIntention.java delete mode 100644 src/main/java/de/marhali/easyi18n/assistance/intention/JsExtractIntention.java create mode 100644 src/main/java/de/marhali/easyi18n/assistance/intention/JsTranslationIntention.java delete mode 100644 src/main/java/de/marhali/easyi18n/assistance/intention/KtExtractIntention.java create mode 100644 src/main/java/de/marhali/easyi18n/assistance/intention/KtTranslationIntention.java create mode 100644 src/main/java/de/marhali/easyi18n/assistance/intention/PhpTranslationIntention.java create mode 100644 src/main/java/de/marhali/easyi18n/assistance/reference/PhpKeyReferenceContributor.java create mode 100644 src/main/resources/META-INF/de.marhali.easyi18n-php.xml diff --git a/CHANGELOG.md b/CHANGELOG.md index 41298bc..b934305 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ ### Added - Key delimiters (namespace / section) can be configured - Extract translation intention +- Full language support for Java, Kotlin, JavaScript / TypeScript, Vue and PHP - Individual icon for tool-window and lookup items - Dedicated configuration file (easy-i18n.xml) inside .idea folder diff --git a/gradle.properties b/gradle.properties index b630b41..6c5ffae 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,7 +17,7 @@ platformVersion = 2021.3 # Plugin Dependencies -> https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html # Example: platformPlugins = com.intellij.java, com.jetbrains.php:203.4449.22 -platformPlugins = org.jetbrains.kotlin, JavaScriptLanguage, org.jetbrains.plugins.vue:213.5744.223 +platformPlugins = org.jetbrains.kotlin, JavaScriptLanguage, org.jetbrains.plugins.vue:213.5744.223, com.jetbrains.php:213.5744.279 # Java language level used to compile sources and to generate the files for - Java 11 is required since 2020.3 javaVersion = 11 diff --git a/src/main/java/de/marhali/easyi18n/assistance/completion/JavaCompletionContributor.java b/src/main/java/de/marhali/easyi18n/assistance/completion/JavaCompletionContributor.java index 8ebc10f..3d2b705 100644 --- a/src/main/java/de/marhali/easyi18n/assistance/completion/JavaCompletionContributor.java +++ b/src/main/java/de/marhali/easyi18n/assistance/completion/JavaCompletionContributor.java @@ -3,14 +3,15 @@ package de.marhali.easyi18n.assistance.completion; import com.intellij.codeInsight.completion.CompletionContributor; import com.intellij.codeInsight.completion.CompletionType; import com.intellij.patterns.PlatformPatterns; -import com.intellij.psi.PsiLiteralValue; +import com.intellij.psi.PsiLiteralExpression; /** - * Java specific completion contributor + * Java specific completion contributor. * @author marhali */ public class JavaCompletionContributor extends CompletionContributor { public JavaCompletionContributor() { - extend(CompletionType.BASIC, PlatformPatterns.psiElement().inside(PsiLiteralValue.class), new KeyCompletionProvider()); + extend(CompletionType.BASIC, PlatformPatterns.psiElement().inside(PsiLiteralExpression.class), + new KeyCompletionProvider()); } } diff --git a/src/main/java/de/marhali/easyi18n/assistance/completion/JsCompletionContributor.java b/src/main/java/de/marhali/easyi18n/assistance/completion/JsCompletionContributor.java new file mode 100644 index 0000000..3154937 --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/assistance/completion/JsCompletionContributor.java @@ -0,0 +1,17 @@ +package de.marhali.easyi18n.assistance.completion; + +import com.intellij.codeInsight.completion.CompletionContributor; +import com.intellij.codeInsight.completion.CompletionType; +import com.intellij.lang.javascript.psi.JSLiteralExpression; +import com.intellij.patterns.PlatformPatterns; + +/** + * JavaScript specific completion contributor. + * @author marhali + */ +public class JsCompletionContributor extends CompletionContributor { + public JsCompletionContributor() { + extend(CompletionType.BASIC, PlatformPatterns.psiElement().inside(JSLiteralExpression.class), + new KeyCompletionProvider()); + } +} \ No newline at end of file diff --git a/src/main/java/de/marhali/easyi18n/assistance/completion/PhpCompletionContributor.java b/src/main/java/de/marhali/easyi18n/assistance/completion/PhpCompletionContributor.java new file mode 100644 index 0000000..f6c76b9 --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/assistance/completion/PhpCompletionContributor.java @@ -0,0 +1,17 @@ +package de.marhali.easyi18n.assistance.completion; + +import com.intellij.codeInsight.completion.CompletionContributor; +import com.intellij.codeInsight.completion.CompletionType; +import com.intellij.patterns.PlatformPatterns; +import com.jetbrains.php.lang.psi.elements.StringLiteralExpression; + +/** + * Php specific completion contributor. + * @author marhali + */ +public class PhpCompletionContributor extends CompletionContributor { + public PhpCompletionContributor() { + extend(CompletionType.BASIC, PlatformPatterns.psiElement().inside(StringLiteralExpression.class), + new KeyCompletionProvider()); + } +} diff --git a/src/main/java/de/marhali/easyi18n/assistance/folding/AbstractFoldingBuilder.java b/src/main/java/de/marhali/easyi18n/assistance/folding/AbstractFoldingBuilder.java index 5637da7..a4387bf 100644 --- a/src/main/java/de/marhali/easyi18n/assistance/folding/AbstractFoldingBuilder.java +++ b/src/main/java/de/marhali/easyi18n/assistance/folding/AbstractFoldingBuilder.java @@ -2,12 +2,17 @@ package de.marhali.easyi18n.assistance.folding; import com.intellij.lang.ASTNode; import com.intellij.lang.folding.FoldingBuilderEx; -import com.intellij.openapi.editor.FoldingGroup; +import com.intellij.lang.folding.FoldingDescriptor; +import com.intellij.openapi.editor.Document; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiElement; import de.marhali.easyi18n.DataStore; import de.marhali.easyi18n.InstanceManager; import de.marhali.easyi18n.assistance.OptionalAssistance; +import de.marhali.easyi18n.model.TranslationData; import de.marhali.easyi18n.model.TranslationValue; import de.marhali.easyi18n.settings.ProjectSettingsService; import de.marhali.easyi18n.util.KeyPathConverter; @@ -15,25 +20,67 @@ import de.marhali.easyi18n.util.KeyPathConverter; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; +import java.util.List; + /** - * Language specific folding of translation key with preferred locale value. + * Language specific translation key folding with representative locale value. * @author marhali */ abstract class AbstractFoldingBuilder extends FoldingBuilderEx implements OptionalAssistance { - - protected static final FoldingGroup group = FoldingGroup.newGroup("EasyI18n key folding"); + /** + * Extract all relevant folding regions for the desired root element. + * The implementation does not need to verify if the character literal is a valid translation. + * @param root Root element + * @return found regions + */ + abstract @NotNull List> extractRegions(@NotNull PsiElement root); /** - * Constructs the folding text for the provided text. - * @param project Opened project - * @param text Designated translation key - * @return Preferred locale value or null if translation does not exists + * Extract the text from the given node. + * @param node Node + * @return extracted text or null if not applicable */ - protected @Nullable String getPlaceholderText(@NotNull Project project, @Nullable String text) { + abstract @Nullable String extractText(@NotNull ASTNode node); + + @Override + public FoldingDescriptor @NotNull [] buildFoldRegions(@NotNull PsiElement root, @NotNull Document document, boolean quick) { + + if(quick || !isAssistance(root.getProject())) { + return FoldingDescriptor.EMPTY; + } + + List descriptors = new ArrayList<>(); + + TranslationData data = InstanceManager.get(root.getProject()).store().getData(); + KeyPathConverter converter = new KeyPathConverter(root.getProject()); + + for(Pair region : extractRegions(root)) { + if(data.getTranslation(converter.fromString(region.first)) == null) { + continue; + } + + TextRange range = new TextRange(region.second.getTextRange().getStartOffset() + 1, + region.second.getTextRange().getEndOffset() - 1); + + // Some language implementations like [Vue Template] does not support FoldingGroup's + FoldingDescriptor descriptor = new FoldingDescriptor(region.second.getNode(), range); + + descriptors.add(descriptor); + } + + return descriptors.toArray(new FoldingDescriptor[0]); + } + + @Override + public @Nullable String getPlaceholderText(@NotNull ASTNode node) { + String text = extractText(node); + if(text == null) { return null; } + Project project = node.getPsi().getProject(); DataStore store = InstanceManager.get(project).store(); KeyPathConverter converter = new KeyPathConverter(project); TranslationValue localeValues = store.getData().getTranslation(converter.fromString(text)); diff --git a/src/main/java/de/marhali/easyi18n/assistance/folding/JavaFoldingBuilder.java b/src/main/java/de/marhali/easyi18n/assistance/folding/JavaFoldingBuilder.java index cac89f3..e4904a5 100644 --- a/src/main/java/de/marhali/easyi18n/assistance/folding/JavaFoldingBuilder.java +++ b/src/main/java/de/marhali/easyi18n/assistance/folding/JavaFoldingBuilder.java @@ -1,66 +1,32 @@ package de.marhali.easyi18n.assistance.folding; import com.intellij.lang.ASTNode; -import com.intellij.lang.folding.FoldingDescriptor; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.Pair; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiLiteralExpression; import com.intellij.psi.util.PsiTreeUtil; -import de.marhali.easyi18n.DataStore; -import de.marhali.easyi18n.InstanceManager; -import de.marhali.easyi18n.util.KeyPathConverter; - import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.Collection; import java.util.List; +import java.util.stream.Collectors; /** * Java specific translation key folding. * @author marhali */ public class JavaFoldingBuilder extends AbstractFoldingBuilder { - @Override - public FoldingDescriptor @NotNull [] buildFoldRegions(@NotNull PsiElement root, @NotNull Document document, boolean quick) { - List descriptors = new ArrayList<>(); - - if(!isAssistance(root.getProject())) { - return FoldingDescriptor.EMPTY; - } - - Collection literalExpressions = - PsiTreeUtil.findChildrenOfType(root, PsiLiteralExpression.class); - - DataStore store = InstanceManager.get(root.getProject()).store(); - KeyPathConverter converter = new KeyPathConverter(root.getProject()); - - for(final PsiLiteralExpression literalExpression : literalExpressions) { - String value = literalExpression.getValue() instanceof String - ? (String) literalExpression.getValue() : null; - - if(value == null || store.getData().getTranslation(converter.fromString(value)) == null) { - continue; - } - - TextRange range = literalExpression.getTextRange(); - FoldingDescriptor descriptor = new FoldingDescriptor(literalExpression.getNode(), - new TextRange(range.getStartOffset() + 1, range.getEndOffset() - 1), group); - - descriptors.add(descriptor); - } - - return descriptors.toArray(new FoldingDescriptor[0]); + @NotNull List> extractRegions(@NotNull PsiElement root) { + return PsiTreeUtil.findChildrenOfType(root, PsiLiteralExpression.class).stream().map(literalExpression -> + Pair.pair(String.valueOf(literalExpression.getValue()), (PsiElement) literalExpression)) + .collect(Collectors.toList()); } @Override - public @Nullable String getPlaceholderText(@NotNull ASTNode node) { + @Nullable String extractText(@NotNull ASTNode node) { PsiLiteralExpression literalExpression = node.getPsi(PsiLiteralExpression.class); - String value = literalExpression.getValue() instanceof String ? (String) literalExpression.getValue() : null; - return getPlaceholderText(literalExpression.getProject(), value); + return String.valueOf(literalExpression.getValue()); } } diff --git a/src/main/java/de/marhali/easyi18n/assistance/folding/JsFoldingBuilder.java b/src/main/java/de/marhali/easyi18n/assistance/folding/JsFoldingBuilder.java new file mode 100644 index 0000000..e96b53a --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/assistance/folding/JsFoldingBuilder.java @@ -0,0 +1,32 @@ +package de.marhali.easyi18n.assistance.folding; + +import com.intellij.lang.ASTNode; +import com.intellij.lang.javascript.psi.JSLiteralExpression; +import com.intellij.openapi.util.Pair; +import com.intellij.psi.PsiElement; +import com.intellij.psi.util.PsiTreeUtil; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * JavaScript specific translation key folding. + * @author marhali + */ +public class JsFoldingBuilder extends AbstractFoldingBuilder { + @Override + @NotNull List> extractRegions(@NotNull PsiElement root) { + return PsiTreeUtil.findChildrenOfType(root, JSLiteralExpression.class).stream().map(literalExpression -> + Pair.pair(literalExpression.getStringValue(), (PsiElement) literalExpression)) + .collect(Collectors.toList()); + } + + @Override + @Nullable String extractText(@NotNull ASTNode node) { + JSLiteralExpression literalExpression = node.getPsi(JSLiteralExpression.class); + return literalExpression.getStringValue(); + } +} diff --git a/src/main/java/de/marhali/easyi18n/assistance/folding/KtFoldingBuilder.java b/src/main/java/de/marhali/easyi18n/assistance/folding/KtFoldingBuilder.java index 014bfa3..bd95f16 100644 --- a/src/main/java/de/marhali/easyi18n/assistance/folding/KtFoldingBuilder.java +++ b/src/main/java/de/marhali/easyi18n/assistance/folding/KtFoldingBuilder.java @@ -1,22 +1,16 @@ package de.marhali.easyi18n.assistance.folding; import com.intellij.lang.ASTNode; -import com.intellij.lang.folding.FoldingDescriptor; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.Pair; import com.intellij.psi.PsiElement; import com.intellij.psi.util.PsiTreeUtil; -import de.marhali.easyi18n.DataStore; -import de.marhali.easyi18n.InstanceManager; -import de.marhali.easyi18n.util.KeyPathConverter; - import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.psi.KtStringTemplateEntry; +import org.jetbrains.kotlin.psi.KtStringTemplateExpression; import java.util.ArrayList; -import java.util.Collection; import java.util.List; /** @@ -25,39 +19,27 @@ import java.util.List; */ public class KtFoldingBuilder extends AbstractFoldingBuilder { @Override - public FoldingDescriptor @NotNull [] buildFoldRegions(@NotNull PsiElement root, @NotNull Document document, boolean quick) { - List descriptors = new ArrayList<>(); + @NotNull List> extractRegions(@NotNull PsiElement root) { + List> regions = new ArrayList<>(); - if(!isAssistance(root.getProject())) { - return FoldingDescriptor.EMPTY; - } - - Collection templateEntries = - PsiTreeUtil.findChildrenOfType(root, KtStringTemplateEntry.class); - - DataStore store = InstanceManager.get(root.getProject()).store(); - KeyPathConverter converter = new KeyPathConverter(root.getProject()); - - for (KtStringTemplateEntry templateEntry : templateEntries) { - String value = templateEntry.getText(); - - if(value == null || store.getData().getTranslation(converter.fromString(value)) == null) { - continue; + for (KtStringTemplateExpression templateExpression : PsiTreeUtil.findChildrenOfType(root, KtStringTemplateExpression.class)) { + for (KtStringTemplateEntry entry : templateExpression.getEntries()) { + regions.add(Pair.pair(entry.getText(), templateExpression)); + break; } - - TextRange range = templateEntry.getTextRange(); - FoldingDescriptor descriptor = new FoldingDescriptor(templateEntry.getNode(), - new TextRange(range.getStartOffset(), range.getEndOffset()), group); - - descriptors.add(descriptor); } - return descriptors.toArray(new FoldingDescriptor[0]); + return regions; } @Override - public @Nullable String getPlaceholderText(@NotNull ASTNode node) { - KtStringTemplateEntry templateEntry = node.getPsi(KtStringTemplateEntry.class); - return getPlaceholderText(templateEntry.getProject(), templateEntry.getText()); + @Nullable String extractText(@NotNull ASTNode node) { + KtStringTemplateExpression templateExpression = node.getPsi(KtStringTemplateExpression.class); + + for (KtStringTemplateEntry entry : templateExpression.getEntries()) { + return entry.getText(); + } + + return null; } } diff --git a/src/main/java/de/marhali/easyi18n/assistance/folding/PhpFoldingBuilder.java b/src/main/java/de/marhali/easyi18n/assistance/folding/PhpFoldingBuilder.java new file mode 100644 index 0000000..f694b3b --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/assistance/folding/PhpFoldingBuilder.java @@ -0,0 +1,31 @@ +package de.marhali.easyi18n.assistance.folding; + +import com.intellij.lang.ASTNode; +import com.intellij.openapi.util.Pair; +import com.intellij.psi.PsiElement; +import com.intellij.psi.util.PsiTreeUtil; +import com.jetbrains.php.lang.psi.elements.StringLiteralExpression; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * Php specific translation key folding. + * @author marhali + */ +public class PhpFoldingBuilder extends AbstractFoldingBuilder { + @Override + @NotNull List> extractRegions(@NotNull PsiElement root) { + return PsiTreeUtil.findChildrenOfType(root, StringLiteralExpression.class).stream().map(literalExpression -> + Pair.pair(literalExpression.getContents(), (PsiElement) literalExpression)) + .collect(Collectors.toList()); + } + + @Override + @Nullable String extractText(@NotNull ASTNode node) { + StringLiteralExpression literalExpression = node.getPsi(StringLiteralExpression.class); + return literalExpression.getContents(); + } +} diff --git a/src/main/java/de/marhali/easyi18n/assistance/intention/AbstractExtractIntention.java b/src/main/java/de/marhali/easyi18n/assistance/intention/AbstractExtractIntention.java deleted file mode 100644 index 2f7c2bf..0000000 --- a/src/main/java/de/marhali/easyi18n/assistance/intention/AbstractExtractIntention.java +++ /dev/null @@ -1,93 +0,0 @@ -package de.marhali.easyi18n.assistance.intention; - -import com.intellij.codeInsight.intention.FileModifier; -import com.intellij.openapi.command.WriteCommandAction; -import com.intellij.openapi.editor.Caret; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.project.Project; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.psi.util.PsiEditorUtil; - -import com.siyeh.ipp.base.MutablyNamedIntention; - -import de.marhali.easyi18n.InstanceManager; -import de.marhali.easyi18n.assistance.OptionalAssistance; -import de.marhali.easyi18n.dialog.AddDialog; -import de.marhali.easyi18n.model.KeyPath; -import de.marhali.easyi18n.model.TranslationData; -import de.marhali.easyi18n.settings.ProjectSettings; -import de.marhali.easyi18n.settings.ProjectSettingsService; -import de.marhali.easyi18n.util.KeyPathConverter; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ResourceBundle; - -/** - * Intention for extracting a translation. Either by translation key or preferred locale value - * @author marhali - */ -abstract class AbstractExtractIntention extends MutablyNamedIntention implements OptionalAssistance { - - protected static final ResourceBundle bundle = ResourceBundle.getBundle("messages"); - - @Override - public @NotNull String getFamilyName() { - return "EasyI18n"; - } - - @Override - public boolean startInWriteAction() { - return false; - } - - protected @NotNull String getTextForElement(@NotNull Project project, @Nullable String key) { - KeyPathConverter converter = new KeyPathConverter(project); - TranslationData data = InstanceManager.get(project).store().getData(); - - return key != null && data.getTranslation(converter.fromString(key)) != null - ? bundle.getString("action.edit") - : bundle.getString("action.extract"); - } - - protected void extractTranslation(@NotNull Project project, @NotNull String text, PsiElement psi) { - ProjectSettings settings = ProjectSettingsService.get(project).getState(); - KeyPathConverter converter = new KeyPathConverter(settings); - - // Extract translation key - // We assume that a text is a translation-key if it contains section delimiters and does not end with them - if(text.contains(settings.getSectionDelimiter()) && !text.endsWith(settings.getSectionDelimiter())) { - new AddDialog(project, converter.fromString(text), null).showAndHandle(); - - } else { // Extract translation value (here preview locale value) - AddDialog dialog = new AddDialog(project, new KeyPath(), text); - - // Replace editor caret with chosen translation key - dialog.registerCallback(translationUpdate -> { - Editor editor = PsiEditorUtil.findEditor(psi); - - if(editor != null) { - Document doc = editor.getDocument(); - Caret caret = editor.getCaretModel().getPrimaryCaret(); - int start = psi.getTextOffset() + 1; - int end = start + text.length(); - - WriteCommandAction.runWriteCommandAction(project, () -> - doc.replaceString(start, end, converter.toString(translationUpdate.getChange().getKey()))); - - caret.removeSelection(); - } - }); - - dialog.showAndHandle();; - } - } - - @Override - public @Nullable FileModifier getFileModifierForPreview(@NotNull PsiFile target) { - return this; - } -} diff --git a/src/main/java/de/marhali/easyi18n/assistance/intention/AbstractTranslationIntention.java b/src/main/java/de/marhali/easyi18n/assistance/intention/AbstractTranslationIntention.java new file mode 100644 index 0000000..99be4c2 --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/assistance/intention/AbstractTranslationIntention.java @@ -0,0 +1,138 @@ +package de.marhali.easyi18n.assistance.intention; + +import com.intellij.codeInsight.intention.BaseElementAtCaretIntentionAction; +import com.intellij.codeInspection.util.IntentionFamilyName; +import com.intellij.codeInspection.util.IntentionName; +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.editor.Caret; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiElement; +import com.intellij.util.IncorrectOperationException; + +import de.marhali.easyi18n.InstanceManager; +import de.marhali.easyi18n.assistance.OptionalAssistance; +import de.marhali.easyi18n.dialog.AddDialog; +import de.marhali.easyi18n.dialog.EditDialog; +import de.marhali.easyi18n.model.KeyPath; +import de.marhali.easyi18n.model.Translation; +import de.marhali.easyi18n.model.TranslationData; +import de.marhali.easyi18n.model.TranslationValue; +import de.marhali.easyi18n.settings.ProjectSettings; +import de.marhali.easyi18n.settings.ProjectSettingsService; +import de.marhali.easyi18n.util.KeyPathConverter; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ResourceBundle; + +/** + * Intention for translation related use-cases. + * Can be used to extract (create) translations or to edit existing ones. + * @author marhali + */ +abstract class AbstractTranslationIntention extends BaseElementAtCaretIntentionAction implements OptionalAssistance { + + protected static final ResourceBundle bundle = ResourceBundle.getBundle("messages"); + + private boolean existingTranslation = false; + + @Override + public @IntentionName @NotNull String getText() { + return existingTranslation + ? bundle.getString("action.edit") + : bundle.getString("action.extract"); + } + + @Override + public @NotNull @IntentionFamilyName String getFamilyName() { + return "EasyI18n"; + } + + @Override + public boolean startInWriteAction() { + return false; + } + + /** + * This is the only method a language-specific translation intention needs to implement. + * The implementation needs to verify element type and extract the relevant key literal or value. + * @param element Element at caret + * @return extract translation key (not verified!) or null if intention is not applicable for this element + */ + protected abstract @Nullable String extractText(@NotNull PsiElement element); + + @NotNull TextRange convertRange(@NotNull TextRange input) { + return new TextRange(input.getStartOffset(), input.getEndOffset()); + } + + @Override + public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) { + if(!isAssistance(project)) { + return false; + } + + String text = extractText(element); + + if(text != null) { + KeyPathConverter converter = new KeyPathConverter(project); + existingTranslation = InstanceManager.get(project).store().getData() + .getTranslation(converter.fromString(text)) != null; + } + + return text != null; + } + + @Override + public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) + throws IncorrectOperationException { + + ProjectSettings settings = ProjectSettingsService.get(project).getState(); + KeyPathConverter converter = new KeyPathConverter(settings); + + String text = extractText(element); + + if(text == null) { + throw new IncorrectOperationException("Cannot extract translation intention at caret"); + } + + TranslationData data = InstanceManager.get(project).store().getData(); + KeyPath path = converter.fromString(text); + TranslationValue existingTranslation = data.getTranslation(path); + + // Existing translation - edit dialog + if(existingTranslation != null) { + new EditDialog(project, new Translation(path, existingTranslation)).showAndHandle(); + return; + } + + // Extract translation by key + // We assume that a text is a translation-key if it contains section delimiters and does not end with them + if(text.contains(settings.getSectionDelimiter()) && !text.endsWith(settings.getSectionDelimiter())) { + new AddDialog(project, path, null).showAndHandle(); + return; + } + + // Extract translation by preview locale value + AddDialog dialog = new AddDialog(project, new KeyPath(), text); + + dialog.registerCallback(translationUpdate -> { // Replace text at caret with chosen translation key + if(editor != null) { + Document doc = editor.getDocument(); + Caret caret = editor.getCaretModel().getPrimaryCaret(); + TextRange range = convertRange(element.getTextRange()); + + WriteCommandAction.runWriteCommandAction(project, () -> + doc.replaceString(range.getStartOffset(), range.getEndOffset(), + converter.toString(translationUpdate.getChange().getKey()))); + + caret.removeSelection(); + } + }); + + dialog.showAndHandle(); + } +} diff --git a/src/main/java/de/marhali/easyi18n/assistance/intention/JavaExtractIntention.java b/src/main/java/de/marhali/easyi18n/assistance/intention/JavaExtractIntention.java deleted file mode 100644 index d074d4f..0000000 --- a/src/main/java/de/marhali/easyi18n/assistance/intention/JavaExtractIntention.java +++ /dev/null @@ -1,35 +0,0 @@ -package de.marhali.easyi18n.assistance.intention; - -import com.intellij.codeInspection.util.IntentionName; -import com.intellij.psi.*; -import com.siyeh.ipp.base.PsiElementPredicate; -import org.jetbrains.annotations.NotNull; - -/** - * Java specific translation key intention. - * @author marhali - */ -public class JavaExtractIntention extends AbstractExtractIntention { - - @Override - protected @IntentionName String getTextForElement(PsiElement element) { - return getTextForElement(element.getProject(), (String) ((PsiLiteralExpression) element).getValue()); - } - - @Override - protected void processIntention(@NotNull PsiElement element) { - System.out.println("proci2"); - if(!(element instanceof PsiLiteralExpression) - || !(((PsiLiteralExpression) element).getValue() instanceof String)) { - return; - } - - extractTranslation(element.getProject(), (String) ((PsiLiteralExpression) element).getValue(), element); - } - - @Override - protected @NotNull PsiElementPredicate getElementPredicate() { - System.out.println("predi2"); - return element -> element instanceof PsiLiteralExpression; - } -} diff --git a/src/main/java/de/marhali/easyi18n/assistance/intention/JavaTranslationIntention.java b/src/main/java/de/marhali/easyi18n/assistance/intention/JavaTranslationIntention.java new file mode 100644 index 0000000..aed1b98 --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/assistance/intention/JavaTranslationIntention.java @@ -0,0 +1,27 @@ +package de.marhali.easyi18n.assistance.intention; + +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.*; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Java specific translation intention. + * @author marhali + */ +public class JavaTranslationIntention extends AbstractTranslationIntention { + @Override + protected @Nullable String extractText(@NotNull PsiElement element) { + if(!(element.getParent() instanceof PsiLiteralExpression)) { + return null; + } + + return String.valueOf(((PsiLiteralExpression) element.getParent()).getValue()); + } + + @Override + @NotNull TextRange convertRange(@NotNull TextRange input) { + return new TextRange(input.getStartOffset() + 1, input.getEndOffset() - 1); + } +} diff --git a/src/main/java/de/marhali/easyi18n/assistance/intention/JsExtractIntention.java b/src/main/java/de/marhali/easyi18n/assistance/intention/JsExtractIntention.java deleted file mode 100644 index 5496802..0000000 --- a/src/main/java/de/marhali/easyi18n/assistance/intention/JsExtractIntention.java +++ /dev/null @@ -1,40 +0,0 @@ -package de.marhali.easyi18n.assistance.intention; - -import com.intellij.codeInspection.util.IntentionName; -import com.intellij.lang.javascript.psi.JSLiteralExpression; -import com.intellij.psi.PsiElement; -import com.siyeh.ipp.base.PsiElementPredicate; -import org.jetbrains.annotations.NotNull; - -/** - * JavaScript specific translation key intention. - * @author marhali - */ -// TODO: current implementation does not support other languages than Java -public class JsExtractIntention extends AbstractExtractIntention { - - @Override - public @NotNull String getFamilyName() { - return "JavaScript"; - } - - @Override - protected @IntentionName String getTextForElement(PsiElement element) { - return "HILFE"; - //return getTextForElement(element.getProject(), ((JSLiteralExpression) element).getStringValue()); - } - - @Override - protected void processIntention(@NotNull PsiElement element) { - if(!(element instanceof JSLiteralExpression) || ((JSLiteralExpression) element).getStringValue() == null) { - return; - } - - extractTranslation(element.getProject(), ((JSLiteralExpression) element).getStringValue(), element); - } - - @Override - protected @NotNull PsiElementPredicate getElementPredicate() { - return element -> true; - } -} diff --git a/src/main/java/de/marhali/easyi18n/assistance/intention/JsTranslationIntention.java b/src/main/java/de/marhali/easyi18n/assistance/intention/JsTranslationIntention.java new file mode 100644 index 0000000..da526a5 --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/assistance/intention/JsTranslationIntention.java @@ -0,0 +1,28 @@ +package de.marhali.easyi18n.assistance.intention; + +import com.intellij.lang.javascript.psi.JSLiteralExpression; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiElement; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * JavaScript specific translation key intention. + * @author marhali + */ +public class JsTranslationIntention extends AbstractTranslationIntention { + @Override + protected @Nullable String extractText(@NotNull PsiElement element) { + if(!(element.getParent() instanceof JSLiteralExpression)) { + return null; + } + + return ((JSLiteralExpression) element.getParent()).getStringValue(); + } + + @Override + @NotNull TextRange convertRange(@NotNull TextRange input) { + return new TextRange(input.getStartOffset() + 1, input.getEndOffset() - 1); + } +} diff --git a/src/main/java/de/marhali/easyi18n/assistance/intention/KtExtractIntention.java b/src/main/java/de/marhali/easyi18n/assistance/intention/KtExtractIntention.java deleted file mode 100644 index bdaf628..0000000 --- a/src/main/java/de/marhali/easyi18n/assistance/intention/KtExtractIntention.java +++ /dev/null @@ -1,41 +0,0 @@ -package de.marhali.easyi18n.assistance.intention; - -import com.intellij.codeInspection.util.IntentionName; -import com.intellij.psi.PsiElement; -import com.siyeh.ipp.base.PsiElementPredicate; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.kotlin.psi.KtLiteralStringTemplateEntry; -import org.jetbrains.kotlin.psi.KtStringTemplateExpression; - -/** - * Kotlin specific translation key intention. - * @author marhali - */ -// TODO: kotlin impl does not work - no action -public class KtExtractIntention extends AbstractExtractIntention { - @Override - protected @IntentionName String getTextForElement(PsiElement element) { - return "hallo"; - //return getTextForElement(element.getProject(), element.getText()); - } - - @Override - protected void processIntention(@NotNull PsiElement element) { - System.out.println("Hallo"); - - if(!(element instanceof KtStringTemplateExpression)) { - System.out.println(element.getClass()); - return; - } - - System.out.println("hallo"); - - extractTranslation(element.getProject() , element.getText(), element); - } - - @Override - protected @NotNull PsiElementPredicate getElementPredicate() { - System.out.println("predi"); - return element -> element instanceof KtLiteralStringTemplateEntry; - } -} diff --git a/src/main/java/de/marhali/easyi18n/assistance/intention/KtTranslationIntention.java b/src/main/java/de/marhali/easyi18n/assistance/intention/KtTranslationIntention.java new file mode 100644 index 0000000..7ed3f31 --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/assistance/intention/KtTranslationIntention.java @@ -0,0 +1,23 @@ +package de.marhali.easyi18n.assistance.intention; + +import com.intellij.psi.PsiElement; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.kotlin.psi.KtLiteralStringTemplateEntry; + +/** + * Kotlin specific translation key intention. + * @author marhali + */ +public class KtTranslationIntention extends AbstractTranslationIntention { + @Override + protected @Nullable String extractText(@NotNull PsiElement element) { + if(!(element.getParent() instanceof KtLiteralStringTemplateEntry)) { + return null; + } + + KtLiteralStringTemplateEntry expression = (KtLiteralStringTemplateEntry) element.getParent(); + return expression.getText(); + } +} diff --git a/src/main/java/de/marhali/easyi18n/assistance/intention/PhpTranslationIntention.java b/src/main/java/de/marhali/easyi18n/assistance/intention/PhpTranslationIntention.java new file mode 100644 index 0000000..76693a3 --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/assistance/intention/PhpTranslationIntention.java @@ -0,0 +1,23 @@ +package de.marhali.easyi18n.assistance.intention; + +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiElement; +import com.jetbrains.php.lang.psi.elements.StringLiteralExpression; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Php specific translation intention + * @author marhali + */ +public class PhpTranslationIntention extends AbstractTranslationIntention { + @Override + protected @Nullable String extractText(@NotNull PsiElement element) { + if(!(element.getParent() instanceof StringLiteralExpression)) { + return null; + } + + return ((StringLiteralExpression) element.getParent()).getContents(); + } +} diff --git a/src/main/java/de/marhali/easyi18n/assistance/reference/JavaKeyReferenceContributor.java b/src/main/java/de/marhali/easyi18n/assistance/reference/JavaKeyReferenceContributor.java index bff2307..7d80e66 100644 --- a/src/main/java/de/marhali/easyi18n/assistance/reference/JavaKeyReferenceContributor.java +++ b/src/main/java/de/marhali/easyi18n/assistance/reference/JavaKeyReferenceContributor.java @@ -4,6 +4,7 @@ import com.intellij.openapi.project.Project; import com.intellij.patterns.PlatformPatterns; import com.intellij.psi.*; + import com.intellij.util.ProcessingContext; import org.jetbrains.annotations.NotNull; @@ -14,12 +15,10 @@ import org.jetbrains.annotations.NotNull; */ public class JavaKeyReferenceContributor extends AbstractKeyReferenceContributor { - // TODO: why not PsiLiteralExpression? - @Override public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) { registrar.registerReferenceProvider( - PlatformPatterns.psiElement(PsiLiteralValue.class), + PlatformPatterns.psiElement(PsiLiteralExpression.class), getProvider()); } @@ -30,8 +29,10 @@ public class JavaKeyReferenceContributor extends AbstractKeyReferenceContributor @NotNull PsiElement element, @NotNull ProcessingContext context) { Project project = element.getProject(); - PsiLiteralValue literalValue = (PsiLiteralValue) element; - String value = literalValue.getValue() instanceof String ? (String) literalValue.getValue() : null; + PsiLiteralExpression literalExpression = (PsiLiteralExpression) element; + String value = literalExpression.getValue() instanceof String + ? (String) literalExpression.getValue() + : null; return getReferences(project, element, value); } diff --git a/src/main/java/de/marhali/easyi18n/assistance/reference/PhpKeyReferenceContributor.java b/src/main/java/de/marhali/easyi18n/assistance/reference/PhpKeyReferenceContributor.java new file mode 100644 index 0000000..0d11599 --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/assistance/reference/PhpKeyReferenceContributor.java @@ -0,0 +1,37 @@ +package de.marhali.easyi18n.assistance.reference; + +import com.intellij.openapi.project.Project; +import com.intellij.patterns.PlatformPatterns; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiReference; +import com.intellij.psi.PsiReferenceProvider; +import com.intellij.psi.PsiReferenceRegistrar; +import com.intellij.util.ProcessingContext; +import com.jetbrains.php.lang.psi.elements.StringLiteralExpression; + +import org.jetbrains.annotations.NotNull; + +/** + * Php specific key reference binding + */ +public class PhpKeyReferenceContributor extends AbstractKeyReferenceContributor { + @Override + public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) { + registrar.registerReferenceProvider( + PlatformPatterns.psiElement(StringLiteralExpression.class), + getProvider()); + } + + private PsiReferenceProvider getProvider() { + return new PsiReferenceProvider() { + @Override + public PsiReference @NotNull [] getReferencesByElement( + @NotNull PsiElement element, @NotNull ProcessingContext context) { + + Project project = element.getProject(); + StringLiteralExpression literalExpression = (StringLiteralExpression) element; + return getReferences(project, element, literalExpression.getContents()); + } + }; + } +} \ No newline at end of file diff --git a/src/main/resources/META-INF/de.marhali.easyi18n-java.xml b/src/main/resources/META-INF/de.marhali.easyi18n-java.xml index 43a9382..e172b2b 100644 --- a/src/main/resources/META-INF/de.marhali.easyi18n-java.xml +++ b/src/main/resources/META-INF/de.marhali.easyi18n-java.xml @@ -1,7 +1,7 @@ - de.marhali.easyi18n.assistance.intention.JavaExtractIntention + de.marhali.easyi18n.assistance.intention.JavaTranslationIntention + + + + + + + + + + + - de.marhali.easyi18n.assistance.intention.JsExtractIntention + de.marhali.easyi18n.assistance.intention.JsTranslationIntention + + \ No newline at end of file diff --git a/src/main/resources/META-INF/de.marhali.easyi18n-kotlin.xml b/src/main/resources/META-INF/de.marhali.easyi18n-kotlin.xml index b1b898b..7e5c8c4 100644 --- a/src/main/resources/META-INF/de.marhali.easyi18n-kotlin.xml +++ b/src/main/resources/META-INF/de.marhali.easyi18n-kotlin.xml @@ -1,7 +1,7 @@ - de.marhali.easyi18n.assistance.intention.KtExtractIntention + de.marhali.easyi18n.assistance.intention.KtTranslationIntention + + + de.marhali.easyi18n.assistance.intention.PhpTranslationIntention + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/META-INF/de.marhali.easyi18n-vue.xml b/src/main/resources/META-INF/de.marhali.easyi18n-vue.xml index 68c3fa0..79ed051 100644 --- a/src/main/resources/META-INF/de.marhali.easyi18n-vue.xml +++ b/src/main/resources/META-INF/de.marhali.easyi18n-vue.xml @@ -1,6 +1,15 @@ - + + + + + \ No newline at end of file diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index b1274f6..4db7d2f 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -13,6 +13,7 @@ JavaScript com.intellij.java org.jetbrains.plugins.vue + com.jetbrains.php Date: Thu, 21 Apr 2022 21:49:06 +0200 Subject: [PATCH 46/51] expand already expanded nodes after data update --- CHANGELOG.md | 1 + .../java/de/marhali/easyi18n/tabs/TreeView.java | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b934305..1b13087 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - Key delimiters (namespace / section) can be configured - Extract translation intention - Full language support for Java, Kotlin, JavaScript / TypeScript, Vue and PHP +- Expand already expanded nodes after data update - Individual icon for tool-window and lookup items - Dedicated configuration file (easy-i18n.xml) inside .idea folder diff --git a/src/main/java/de/marhali/easyi18n/tabs/TreeView.java b/src/main/java/de/marhali/easyi18n/tabs/TreeView.java index 049f516..7db7850 100644 --- a/src/main/java/de/marhali/easyi18n/tabs/TreeView.java +++ b/src/main/java/de/marhali/easyi18n/tabs/TreeView.java @@ -32,6 +32,8 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.TreePath; +import java.util.ArrayList; +import java.util.List; import java.util.ResourceBundle; /** @@ -84,7 +86,21 @@ public class TreeView implements BusListener { @Override public void onUpdateData(@NotNull TranslationData data) { + List expanded = getExpandedRows(); tree.setModel(this.currentMapper = new TreeModelMapper(data, ProjectSettingsService.get(project).getState())); + expanded.forEach(tree::expandRow); + } + + private List getExpandedRows() { + List expanded = new ArrayList<>(); + + for(int i = 0; i < tree.getRowCount(); i++) { + if(tree.isExpanded(i)) { + expanded.add(i); + } + } + + return expanded; } @Override From 091acb2e32361127c774c40d8e45043d0eab903e Mon Sep 17 00:00:00 2001 From: marhali Date: Thu, 21 Apr 2022 22:07:56 +0200 Subject: [PATCH 47/51] remove unused translation pack --- src/main/resources/messages_de.properties | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/main/resources/messages_de.properties diff --git a/src/main/resources/messages_de.properties b/src/main/resources/messages_de.properties deleted file mode 100644 index e69de29..0000000 From 433ce8e8449a9024eb80c59c6478f61518db4d00 Mon Sep 17 00:00:00 2001 From: marhali Date: Thu, 21 Apr 2022 22:17:16 +0200 Subject: [PATCH 48/51] add option to force key folding --- CHANGELOG.md | 1 + .../assistance/folding/AbstractFoldingBuilder.java | 8 ++++++-- .../marhali/easyi18n/settings/ProjectSettings.java | 3 +++ .../easyi18n/settings/ProjectSettingsComponent.java | 9 +++++++++ .../settings/ProjectSettingsComponentState.java | 7 +++++++ .../easyi18n/settings/ProjectSettingsState.java | 12 ++++++++++++ .../easyi18n/settings/presets/DefaultPreset.java | 5 +++++ .../settings/presets/ReactI18NextPreset.java | 5 +++++ .../easyi18n/settings/presets/VueI18nPreset.java | 5 +++++ src/main/resources/messages.properties | 4 ++++ 10 files changed, 57 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b13087..97c8a70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - Extract translation intention - Full language support for Java, Kotlin, JavaScript / TypeScript, Vue and PHP - Expand already expanded nodes after data update +- Experimental option to force translation key folding - Individual icon for tool-window and lookup items - Dedicated configuration file (easy-i18n.xml) inside .idea folder diff --git a/src/main/java/de/marhali/easyi18n/assistance/folding/AbstractFoldingBuilder.java b/src/main/java/de/marhali/easyi18n/assistance/folding/AbstractFoldingBuilder.java index a4387bf..691e30f 100644 --- a/src/main/java/de/marhali/easyi18n/assistance/folding/AbstractFoldingBuilder.java +++ b/src/main/java/de/marhali/easyi18n/assistance/folding/AbstractFoldingBuilder.java @@ -14,6 +14,7 @@ import de.marhali.easyi18n.InstanceManager; import de.marhali.easyi18n.assistance.OptionalAssistance; import de.marhali.easyi18n.model.TranslationData; import de.marhali.easyi18n.model.TranslationValue; +import de.marhali.easyi18n.settings.ProjectSettings; import de.marhali.easyi18n.settings.ProjectSettingsService; import de.marhali.easyi18n.util.KeyPathConverter; @@ -22,6 +23,7 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; +import java.util.Set; /** * Language specific translation key folding with representative locale value. @@ -52,8 +54,9 @@ abstract class AbstractFoldingBuilder extends FoldingBuilderEx implements Option List descriptors = new ArrayList<>(); + ProjectSettings settings = ProjectSettingsService.get(root.getProject()).getState(); TranslationData data = InstanceManager.get(root.getProject()).store().getData(); - KeyPathConverter converter = new KeyPathConverter(root.getProject()); + KeyPathConverter converter = new KeyPathConverter(settings); for(Pair region : extractRegions(root)) { if(data.getTranslation(converter.fromString(region.first)) == null) { @@ -64,7 +67,8 @@ abstract class AbstractFoldingBuilder extends FoldingBuilderEx implements Option region.second.getTextRange().getEndOffset() - 1); // Some language implementations like [Vue Template] does not support FoldingGroup's - FoldingDescriptor descriptor = new FoldingDescriptor(region.second.getNode(), range); + FoldingDescriptor descriptor = new FoldingDescriptor(region.second.getNode(), range, + null, Set.of(), settings.isAlwaysFold()); descriptors.add(descriptor); } diff --git a/src/main/java/de/marhali/easyi18n/settings/ProjectSettings.java b/src/main/java/de/marhali/easyi18n/settings/ProjectSettings.java index 5b73e9d..e4c94a6 100644 --- a/src/main/java/de/marhali/easyi18n/settings/ProjectSettings.java +++ b/src/main/java/de/marhali/easyi18n/settings/ProjectSettings.java @@ -29,4 +29,7 @@ public interface ProjectSettings { boolean isNestedKeys(); boolean isAssistance(); + + // Experimental Configuration + boolean isAlwaysFold(); } diff --git a/src/main/java/de/marhali/easyi18n/settings/ProjectSettingsComponent.java b/src/main/java/de/marhali/easyi18n/settings/ProjectSettingsComponent.java index cb84f19..98b1dee 100644 --- a/src/main/java/de/marhali/easyi18n/settings/ProjectSettingsComponent.java +++ b/src/main/java/de/marhali/easyi18n/settings/ProjectSettingsComponent.java @@ -60,6 +60,9 @@ public class ProjectSettingsComponent extends ProjectSettingsComponentState { .addVerticalGap(12) .addComponent(constructNestedKeysField()) .addComponent(constructAssistanceField()) + .addVerticalGap(24) + .addComponent(new TitledSeparator(bundle.getString("settings.experimental.title"))) + .addComponent(constructAlwaysFoldField()) .addComponentFillVertically(new JPanel(), 0) .getPanel(); } @@ -203,6 +206,12 @@ public class ProjectSettingsComponent extends ProjectSettingsComponentState { return assistance; } + private JComponent constructAlwaysFoldField() { + alwaysFold = new JBCheckBox(bundle.getString("settings.experimental.always-fold.title")); + alwaysFold.setToolTipText(bundle.getString("settings.experimental.always-fold.tooltip")); + return alwaysFold; + } + private ItemListener handleParserChange() { return e -> { if(e.getStateChange() == ItemEvent.SELECTED) { diff --git a/src/main/java/de/marhali/easyi18n/settings/ProjectSettingsComponentState.java b/src/main/java/de/marhali/easyi18n/settings/ProjectSettingsComponentState.java index f3dd07e..bf682e8 100644 --- a/src/main/java/de/marhali/easyi18n/settings/ProjectSettingsComponentState.java +++ b/src/main/java/de/marhali/easyi18n/settings/ProjectSettingsComponentState.java @@ -36,6 +36,9 @@ public class ProjectSettingsComponentState { protected JCheckBox nestedKeys; protected JCheckBox assistance; + // Experimental configuration + protected JCheckBox alwaysFold; + protected ProjectSettingsState getState() { // Every field needs to provide its state ProjectSettingsState state = new ProjectSettingsState(); @@ -57,6 +60,8 @@ public class ProjectSettingsComponentState { state.setNestedKeys(nestedKeys.isSelected()); state.setAssistance(assistance.isSelected()); + state.setAlwaysFold(alwaysFold.isSelected()); + return state; } @@ -78,5 +83,7 @@ public class ProjectSettingsComponentState { nestedKeys.setSelected(state.isNestedKeys()); assistance.setSelected(state.isAssistance()); + + alwaysFold.setSelected(state.isAlwaysFold()); } } diff --git a/src/main/java/de/marhali/easyi18n/settings/ProjectSettingsState.java b/src/main/java/de/marhali/easyi18n/settings/ProjectSettingsState.java index d4a077d..29bdd44 100644 --- a/src/main/java/de/marhali/easyi18n/settings/ProjectSettingsState.java +++ b/src/main/java/de/marhali/easyi18n/settings/ProjectSettingsState.java @@ -34,6 +34,9 @@ public class ProjectSettingsState implements ProjectSettings { private Boolean nestedKeys; private Boolean assistance; + // Experimental configuration + private Boolean alwaysFold; + public ProjectSettingsState() {} @Override @@ -102,6 +105,11 @@ public class ProjectSettingsState implements ProjectSettings { return assistance != null ? assistance : defaults.isAssistance(); } + @Override + public boolean isAlwaysFold() { + return alwaysFold != null ? alwaysFold : defaults.isAlwaysFold(); + } + public void setLocalesDirectory(String localesDirectory) { this.localesDirectory = localesDirectory; } @@ -153,4 +161,8 @@ public class ProjectSettingsState implements ProjectSettings { public void setAssistance(Boolean assistance) { this.assistance = assistance; } + + public void setAlwaysFold(Boolean alwaysFold) { + this.alwaysFold = alwaysFold; + } } diff --git a/src/main/java/de/marhali/easyi18n/settings/presets/DefaultPreset.java b/src/main/java/de/marhali/easyi18n/settings/presets/DefaultPreset.java index 124f1d9..63b7b4e 100644 --- a/src/main/java/de/marhali/easyi18n/settings/presets/DefaultPreset.java +++ b/src/main/java/de/marhali/easyi18n/settings/presets/DefaultPreset.java @@ -76,4 +76,9 @@ public class DefaultPreset implements ProjectSettings { public boolean isAssistance() { return true; } + + @Override + public boolean isAlwaysFold() { + return false; + } } diff --git a/src/main/java/de/marhali/easyi18n/settings/presets/ReactI18NextPreset.java b/src/main/java/de/marhali/easyi18n/settings/presets/ReactI18NextPreset.java index 8d5dbeb..cc55f1d 100644 --- a/src/main/java/de/marhali/easyi18n/settings/presets/ReactI18NextPreset.java +++ b/src/main/java/de/marhali/easyi18n/settings/presets/ReactI18NextPreset.java @@ -76,4 +76,9 @@ public class ReactI18NextPreset implements ProjectSettings { public boolean isAssistance() { return true; } + + @Override + public boolean isAlwaysFold() { + return false; + } } diff --git a/src/main/java/de/marhali/easyi18n/settings/presets/VueI18nPreset.java b/src/main/java/de/marhali/easyi18n/settings/presets/VueI18nPreset.java index ea6d064..01927d8 100644 --- a/src/main/java/de/marhali/easyi18n/settings/presets/VueI18nPreset.java +++ b/src/main/java/de/marhali/easyi18n/settings/presets/VueI18nPreset.java @@ -75,4 +75,9 @@ public class VueI18nPreset implements ProjectSettings { public boolean isAssistance() { return true; } + + @Override + public boolean isAlwaysFold() { + return false; + } } diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties index 7986043..3df89be 100644 --- a/src/main/resources/messages.properties +++ b/src/main/resources/messages.properties @@ -52,6 +52,10 @@ settings.editor.key.nesting.title=Escape section delimiter within a section laye settings.editor.key.nesting.tooltip=Escapes the section delimiter within a section to properly reconstruct nested key sections. Disable this feature if nested key sections are the exception rather than the rule in your translation file. settings.editor.assistance.title=Editor code assistance for translations settings.editor.assistance.tooltip=Activates editor support to reference, auto-complete and folding of existing translations. +# Experimental configuration +settings.experimental.title=Experimental Configuration +settings.experimental.always-fold.title=Always fold translation keys +settings.experimental.always-fold.tooltip=Forces the editor to always display the value behind a translation key. The value cannot be unfolded when this function is active. error.io=An error occurred while processing translation files. \n\ Config: {0} => {1} ({2}) \n\ Path: {3} \n\ From 089afec0fed4345eda82d21a20363214a93e1b83 Mon Sep 17 00:00:00 2001 From: marhali Date: Thu, 21 Apr 2022 22:19:14 +0200 Subject: [PATCH 49/51] prepare major release candidate --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 6c5ffae..1486146 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ pluginGroup = de.marhali.easyi18n pluginName = easy-i18n # SemVer format -> https://semver.org -pluginVersion = 3.2.0 +pluginVersion = 4.0.0-rc.1 # See https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html # for insight into build numbers and IntelliJ Platform versions. From bd1375769aecb09c3133f1050773d37b087ac300 Mon Sep 17 00:00:00 2001 From: marhali Date: Thu, 21 Apr 2022 22:22:45 +0200 Subject: [PATCH 50/51] update roadmap --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index bfd9fb2..49b6c25 100644 --- a/README.md +++ b/README.md @@ -91,11 +91,11 @@ _For more examples, please refer to the [Examples Directory](https://github.com/ ## Roadmap - [X] JSON5 Support +- [X] Configurable namespace and section separators +- [X] Define default namespace to use if none was provided +- [X] Enhance editor code assistance - [ ] XML Support - [ ] Mark duplicate translation values -- [ ] Configurable namespace and section separators -- [ ] Define default namespace to use if none was provided -- [ ] Enhance editor code assistance See the [open issues](https://github.com/marhali/easy-i18n/issues) for a full list of proposed features (and known issues). From d58a6478a98108594a90908054322b7318a31fc7 Mon Sep 17 00:00:00 2001 From: marhali Date: Fri, 22 Apr 2022 09:49:45 +0200 Subject: [PATCH 51/51] fix unit tests add new configuration option --- src/test/java/de/marhali/easyi18n/KeyPathConverterTest.java | 5 +++++ .../de/marhali/easyi18n/mapper/PropertiesMapperTest.java | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/test/java/de/marhali/easyi18n/KeyPathConverterTest.java b/src/test/java/de/marhali/easyi18n/KeyPathConverterTest.java index fc71352..d61ea47 100644 --- a/src/test/java/de/marhali/easyi18n/KeyPathConverterTest.java +++ b/src/test/java/de/marhali/easyi18n/KeyPathConverterTest.java @@ -157,6 +157,11 @@ public class KeyPathConverterTest { public boolean isAssistance() { return false; } + + @Override + public boolean isAlwaysFold() { + return false; + } }); } } diff --git a/src/test/java/de/marhali/easyi18n/mapper/PropertiesMapperTest.java b/src/test/java/de/marhali/easyi18n/mapper/PropertiesMapperTest.java index 14aa03a..5667613 100644 --- a/src/test/java/de/marhali/easyi18n/mapper/PropertiesMapperTest.java +++ b/src/test/java/de/marhali/easyi18n/mapper/PropertiesMapperTest.java @@ -230,6 +230,11 @@ public class PropertiesMapperTest extends AbstractMapperTest { public boolean isAssistance() { return false; } + + @Override + public boolean isAlwaysFold() { + return false; + } }); } } \ No newline at end of file