commit
bad6c27386
18
CHANGELOG.md
18
CHANGELOG.md
@ -3,6 +3,24 @@
|
|||||||
# easy-i18n Changelog
|
# easy-i18n Changelog
|
||||||
|
|
||||||
## [Unreleased]
|
## [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
|
||||||
|
- 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 <kbd>.idea</kbd> folder
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Editor assistance has been reengineered. This will affect key suggestion and annotation
|
||||||
|
- Moved configuration dialog into own page inside <kbd>IDE Settings</kbd>
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- AlreadyDisposedException on FileChangeListener after project dispose
|
||||||
|
|
||||||
## [3.2.0]
|
## [3.2.0]
|
||||||
### Added
|
### Added
|
||||||
|
@ -91,6 +91,9 @@ _For more examples, please refer to the [Examples Directory](https://github.com/
|
|||||||
## Roadmap
|
## Roadmap
|
||||||
|
|
||||||
- [X] JSON5 Support
|
- [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
|
- [ ] XML Support
|
||||||
- [ ] Mark duplicate translation values
|
- [ ] Mark duplicate translation values
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
pluginGroup = de.marhali.easyi18n
|
pluginGroup = de.marhali.easyi18n
|
||||||
pluginName = easy-i18n
|
pluginName = easy-i18n
|
||||||
# SemVer format -> https://semver.org
|
# 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
|
# See https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
|
||||||
# for insight into build numbers and IntelliJ Platform versions.
|
# for insight into build numbers and IntelliJ Platform versions.
|
||||||
@ -17,7 +17,7 @@ platformVersion = 2021.3
|
|||||||
|
|
||||||
# Plugin Dependencies -> https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html
|
# Plugin Dependencies -> https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html
|
||||||
# Example: platformPlugins = com.intellij.java, com.jetbrains.php:203.4449.22
|
# 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
|
# Java language level used to compile sources and to generate the files for - Java 11 is required since 2020.3
|
||||||
javaVersion = 11
|
javaVersion = 11
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package de.marhali.easyi18n;
|
package de.marhali.easyi18n;
|
||||||
|
|
||||||
import de.marhali.easyi18n.model.KeyPath;
|
|
||||||
import de.marhali.easyi18n.model.bus.BusListener;
|
import de.marhali.easyi18n.model.bus.BusListener;
|
||||||
import de.marhali.easyi18n.model.TranslationData;
|
import de.marhali.easyi18n.model.TranslationData;
|
||||||
|
|
||||||
|
import de.marhali.easyi18n.model.KeyPath;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
@ -7,10 +7,10 @@ import com.intellij.openapi.vfs.*;
|
|||||||
|
|
||||||
import de.marhali.easyi18n.exception.EmptyLocalesDirException;
|
import de.marhali.easyi18n.exception.EmptyLocalesDirException;
|
||||||
import de.marhali.easyi18n.io.IOHandler;
|
import de.marhali.easyi18n.io.IOHandler;
|
||||||
import de.marhali.easyi18n.model.SettingsState;
|
|
||||||
import de.marhali.easyi18n.model.TranslationData;
|
import de.marhali.easyi18n.model.TranslationData;
|
||||||
import de.marhali.easyi18n.service.FileChangeListener;
|
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 de.marhali.easyi18n.util.NotificationHelper;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@ -35,7 +35,7 @@ public class DataStore {
|
|||||||
this.changeListener = new FileChangeListener(project);
|
this.changeListener = new FileChangeListener(project);
|
||||||
|
|
||||||
VirtualFileManager.getInstance().addAsyncFileListener(
|
VirtualFileManager.getInstance().addAsyncFileListener(
|
||||||
this.changeListener, Disposer.newDisposable("EasyI18n"));
|
this.changeListener, Disposer.newDisposable(project, "EasyI18n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NotNull TranslationData getData() {
|
public @NotNull TranslationData getData() {
|
||||||
@ -48,18 +48,18 @@ public class DataStore {
|
|||||||
* @param successResult Consumer will inform if operation was successful
|
* @param successResult Consumer will inform if operation was successful
|
||||||
*/
|
*/
|
||||||
public void loadFromPersistenceLayer(@NotNull Consumer<Boolean> successResult) {
|
public void loadFromPersistenceLayer(@NotNull Consumer<Boolean> 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().saveAll(); // Save opened files (required if new locales were added)
|
||||||
|
|
||||||
ApplicationManager.getApplication().runReadAction(() -> {
|
ApplicationManager.getApplication().runReadAction(() -> {
|
||||||
try {
|
try {
|
||||||
this.data = new IOHandler(settings).read();
|
this.data = new IOHandler(settings).read();
|
||||||
this.changeListener.updateLocalesPath(settings.getLocalesPath());
|
this.changeListener.updateLocalesPath(settings.getLocalesDirectory());
|
||||||
successResult.accept(true);
|
successResult.accept(true);
|
||||||
|
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
this.data = new TranslationData(settings.isSortKeys());
|
this.data = new TranslationData(settings.isSorting());
|
||||||
successResult.accept(false);
|
successResult.accept(false);
|
||||||
|
|
||||||
if(ex instanceof EmptyLocalesDirException) {
|
if(ex instanceof EmptyLocalesDirException) {
|
||||||
@ -76,7 +76,7 @@ public class DataStore {
|
|||||||
* @param successResult Consumer will inform if operation was successful
|
* @param successResult Consumer will inform if operation was successful
|
||||||
*/
|
*/
|
||||||
public void saveToPersistenceLayer(@NotNull Consumer<Boolean> successResult) {
|
public void saveToPersistenceLayer(@NotNull Consumer<Boolean> successResult) {
|
||||||
SettingsState settings = SettingsService.getInstance(this.project).getState();
|
ProjectSettings settings = ProjectSettingsService.get(project).getState();
|
||||||
|
|
||||||
ApplicationManager.getApplication().runWriteAction(() -> {
|
ApplicationManager.getApplication().runWriteAction(() -> {
|
||||||
try {
|
try {
|
||||||
|
@ -3,7 +3,7 @@ package de.marhali.easyi18n;
|
|||||||
import com.intellij.openapi.application.ApplicationManager;
|
import com.intellij.openapi.application.ApplicationManager;
|
||||||
import com.intellij.openapi.project.Project;
|
import com.intellij.openapi.project.Project;
|
||||||
|
|
||||||
import de.marhali.easyi18n.model.TranslationUpdate;
|
import de.marhali.easyi18n.model.action.TranslationUpdate;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
@ -52,13 +52,22 @@ public class InstanceManager {
|
|||||||
return this.bus;
|
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) {
|
public void processUpdate(TranslationUpdate update) {
|
||||||
if(update.isDeletion() || update.isKeyChange()) { // Remove origin translation
|
if(update.isDeletion() || update.isKeyChange()) { // Remove origin translation
|
||||||
this.store.getData().setTranslation(update.getOrigin().getKey(), null);
|
this.store.getData().setTranslation(update.getOrigin().getKey(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!update.isDeletion()) { // Create or re-create translation with changed data
|
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 -> {
|
this.store.saveToPersistenceLayer(success -> {
|
||||||
|
@ -6,10 +6,10 @@ import com.intellij.openapi.actionSystem.AnActionEvent;
|
|||||||
import com.intellij.openapi.project.Project;
|
import com.intellij.openapi.project.Project;
|
||||||
import com.intellij.ui.content.Content;
|
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.dialog.AddDialog;
|
||||||
|
import de.marhali.easyi18n.model.KeyPath;
|
||||||
|
import de.marhali.easyi18n.service.WindowManager;
|
||||||
|
import de.marhali.easyi18n.util.KeyPathConverter;
|
||||||
import de.marhali.easyi18n.util.TreeUtil;
|
import de.marhali.easyi18n.util.TreeUtil;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@ -32,7 +32,7 @@ public class AddAction extends AnAction {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
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) {
|
private @Nullable KeyPath detectPreKey(@NotNull Project project) {
|
||||||
@ -58,7 +58,7 @@ public class AddAction extends AnAction {
|
|||||||
|
|
||||||
if(row >= 0) {
|
if(row >= 0) {
|
||||||
String path = String.valueOf(window.getTableView().getTable().getValueAt(row, 0));
|
String path = String.valueOf(window.getTableView().getTable().getValueAt(row, 0));
|
||||||
return converter.split(path);
|
return converter.fromString(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ import de.marhali.easyi18n.InstanceManager;
|
|||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -23,9 +24,6 @@ public class ReloadAction extends AnAction {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||||
InstanceManager manager = InstanceManager.get(e.getProject());
|
InstanceManager.get(Objects.requireNonNull(e.getProject())).reload();
|
||||||
manager.store().loadFromPersistenceLayer((success) -> {
|
|
||||||
manager.bus().propagate().onUpdateData(manager.store().getData());
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -3,7 +3,10 @@ package de.marhali.easyi18n.action;
|
|||||||
import com.intellij.icons.AllIcons;
|
import com.intellij.icons.AllIcons;
|
||||||
import com.intellij.openapi.actionSystem.AnAction;
|
import com.intellij.openapi.actionSystem.AnAction;
|
||||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
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 org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
@ -21,6 +24,6 @@ public class SettingsAction extends AnAction {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||||
new SettingsDialog(e.getProject()).showAndHandle();
|
ShowSettingsUtil.getInstance().showSettingsDialog(e.getProject(), ProjectSettingsConfigurable.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -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.intellij.psi.PsiLiteralExpression;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java specific completion contributor.
|
||||||
|
* @author marhali
|
||||||
|
*/
|
||||||
|
public class JavaCompletionContributor extends CompletionContributor {
|
||||||
|
public JavaCompletionContributor() {
|
||||||
|
extend(CompletionType.BASIC, PlatformPatterns.psiElement().inside(PsiLiteralExpression.class),
|
||||||
|
new KeyCompletionProvider());
|
||||||
|
}
|
||||||
|
}
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
@ -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<CompletionParameters> 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<KeyPath> 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);
|
||||||
|
}
|
||||||
|
}
|
@ -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.CompletionContributor;
|
||||||
import com.intellij.codeInsight.completion.CompletionType;
|
import com.intellij.codeInsight.completion.CompletionType;
|
||||||
import com.intellij.patterns.PlatformPatterns;
|
import com.intellij.patterns.PlatformPatterns;
|
||||||
|
|
||||||
import de.marhali.easyi18n.editor.KeyCompletionProvider;
|
|
||||||
import org.jetbrains.kotlin.psi.KtLiteralStringTemplateEntry;
|
import org.jetbrains.kotlin.psi.KtLiteralStringTemplateEntry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Kotlin specific translation key completion contributor.
|
* Kotlin specific completion contributor.
|
||||||
* @author marhali
|
* @author marhali
|
||||||
*/
|
*/
|
||||||
public class KotlinKeyCompletionContributor extends CompletionContributor {
|
public class KtCompletionContributor extends CompletionContributor {
|
||||||
|
public KtCompletionContributor() {
|
||||||
public KotlinKeyCompletionContributor() {
|
|
||||||
extend(CompletionType.BASIC, PlatformPatterns.psiElement().inside(KtLiteralStringTemplateEntry.class),
|
extend(CompletionType.BASIC, PlatformPatterns.psiElement().inside(KtLiteralStringTemplateEntry.class),
|
||||||
new KeyCompletionProvider());
|
new KeyCompletionProvider());
|
||||||
}
|
}
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
@ -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<String, String> results = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
// Filter results for matching leafs (contextual and pluralization support)
|
||||||
|
for (Map.Entry<String, TranslationNode> 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("<strong>").append(results.values().toArray()[0]).append("</strong>");
|
||||||
|
builder.append(DocumentationMarkup.CONTENT_END);
|
||||||
|
|
||||||
|
} else { // Pluralization | Contextual relevant values
|
||||||
|
builder.append(DocumentationMarkup.SECTIONS_START);
|
||||||
|
|
||||||
|
for (Map.Entry<String, String> entry : results.entrySet()) {
|
||||||
|
builder.append(DocumentationMarkup.SECTION_HEADER_START);
|
||||||
|
builder.append(entry.getKey()).append(":");
|
||||||
|
builder.append(DocumentationMarkup.SECTION_SEPARATOR);
|
||||||
|
builder.append("<p>");
|
||||||
|
builder.append("<strong>").append(entry.getValue()).append("</strong>");
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.append(DocumentationMarkup.SECTIONS_END);
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,104 @@
|
|||||||
|
package de.marhali.easyi18n.assistance.folding;
|
||||||
|
|
||||||
|
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.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.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.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Language specific translation key folding with representative locale value.
|
||||||
|
* @author marhali
|
||||||
|
*/
|
||||||
|
abstract class AbstractFoldingBuilder extends FoldingBuilderEx implements OptionalAssistance {
|
||||||
|
/**
|
||||||
|
* 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<Pair<String, PsiElement>> extractRegions(@NotNull PsiElement root);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract the text from the given node.
|
||||||
|
* @param node Node
|
||||||
|
* @return extracted text or null if not applicable
|
||||||
|
*/
|
||||||
|
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<FoldingDescriptor> descriptors = new ArrayList<>();
|
||||||
|
|
||||||
|
ProjectSettings settings = ProjectSettingsService.get(root.getProject()).getState();
|
||||||
|
TranslationData data = InstanceManager.get(root.getProject()).store().getData();
|
||||||
|
KeyPathConverter converter = new KeyPathConverter(settings);
|
||||||
|
|
||||||
|
for(Pair<String, PsiElement> 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,
|
||||||
|
null, Set.of(), settings.isAlwaysFold());
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
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.PsiLiteralExpression;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java specific translation key folding.
|
||||||
|
* @author marhali
|
||||||
|
*/
|
||||||
|
public class JavaFoldingBuilder extends AbstractFoldingBuilder {
|
||||||
|
@Override
|
||||||
|
@NotNull List<Pair<String, PsiElement>> 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
|
||||||
|
@Nullable String extractText(@NotNull ASTNode node) {
|
||||||
|
PsiLiteralExpression literalExpression = node.getPsi(PsiLiteralExpression.class);
|
||||||
|
return String.valueOf(literalExpression.getValue());
|
||||||
|
}
|
||||||
|
}
|
@ -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<Pair<String, PsiElement>> 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();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
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 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.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kotlin specific translation-key folding.
|
||||||
|
* @author marhali
|
||||||
|
*/
|
||||||
|
public class KtFoldingBuilder extends AbstractFoldingBuilder {
|
||||||
|
@Override
|
||||||
|
@NotNull List<Pair<String, PsiElement>> extractRegions(@NotNull PsiElement root) {
|
||||||
|
List<Pair<String, PsiElement>> regions = new ArrayList<>();
|
||||||
|
|
||||||
|
for (KtStringTemplateExpression templateExpression : PsiTreeUtil.findChildrenOfType(root, KtStringTemplateExpression.class)) {
|
||||||
|
for (KtStringTemplateEntry entry : templateExpression.getEntries()) {
|
||||||
|
regions.add(Pair.pair(entry.getText(), templateExpression));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return regions;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable String extractText(@NotNull ASTNode node) {
|
||||||
|
KtStringTemplateExpression templateExpression = node.getPsi(KtStringTemplateExpression.class);
|
||||||
|
|
||||||
|
for (KtStringTemplateEntry entry : templateExpression.getEntries()) {
|
||||||
|
return entry.getText();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -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<Pair<String, PsiElement>> 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();
|
||||||
|
}
|
||||||
|
}
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
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 {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) {
|
||||||
|
registrar.registerReferenceProvider(
|
||||||
|
PlatformPatterns.psiElement(PsiLiteralExpression.class),
|
||||||
|
getProvider());
|
||||||
|
}
|
||||||
|
|
||||||
|
private PsiReferenceProvider getProvider() {
|
||||||
|
return new PsiReferenceProvider() {
|
||||||
|
@Override
|
||||||
|
public PsiReference @NotNull [] getReferencesByElement(
|
||||||
|
@NotNull PsiElement element, @NotNull ProcessingContext context) {
|
||||||
|
|
||||||
|
Project project = element.getProject();
|
||||||
|
PsiLiteralExpression literalExpression = (PsiLiteralExpression) element;
|
||||||
|
String value = literalExpression.getValue() instanceof String
|
||||||
|
? (String) literalExpression.getValue()
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return getReferences(project, element, value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -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<PsiElement> 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());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -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());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -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<PsiElement> {
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,112 +3,65 @@ package de.marhali.easyi18n.dialog;
|
|||||||
import com.intellij.openapi.project.Project;
|
import com.intellij.openapi.project.Project;
|
||||||
import com.intellij.openapi.ui.DialogBuilder;
|
import com.intellij.openapi.ui.DialogBuilder;
|
||||||
import com.intellij.openapi.ui.DialogWrapper;
|
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.action.TranslationCreate;
|
||||||
import de.marhali.easyi18n.model.*;
|
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;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import javax.swing.*;
|
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
|
* @author marhali
|
||||||
*/
|
*/
|
||||||
public class AddDialog {
|
public class AddDialog extends TranslationDialog {
|
||||||
|
|
||||||
private final @NotNull Project project;
|
/**
|
||||||
private final @NotNull KeyPathConverter converter;
|
* Constructs a new create dialog with prefilled fields
|
||||||
|
* @param project Opened project
|
||||||
private @NotNull KeyPath preKey;
|
* @param prefillKey Prefill translation key
|
||||||
|
* @param prefillLocale Prefill preview locale value
|
||||||
private JBTextField keyTextField;
|
*/
|
||||||
private Map<String, JBTextField> valueTextFields;
|
public AddDialog(@NotNull Project project, @Nullable KeyPath prefillKey, @Nullable String prefillLocale) {
|
||||||
|
super(project, new Translation(prefillKey != null ? prefillKey : new KeyPath(),
|
||||||
public AddDialog(@NotNull Project project, @Nullable KeyPath preKey) {
|
prefillLocale != null
|
||||||
this(project);
|
? new TranslationValue(ProjectSettingsService.get(project).getState().getPreviewLocale(), prefillLocale)
|
||||||
this.preKey = preKey == null ? new KeyPath() : preKey;
|
: null)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new create dialog without prefilled fields.
|
||||||
|
* @param project Opened project
|
||||||
|
*/
|
||||||
public AddDialog(@NotNull Project project) {
|
public AddDialog(@NotNull Project project) {
|
||||||
this.project = project;
|
this(project, new KeyPath(), "");
|
||||||
this.converter = new KeyPathConverter(project);
|
|
||||||
this.preKey = 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();
|
DialogBuilder builder = new DialogBuilder();
|
||||||
builder.setTitle(ResourceBundle.getBundle("messages").getString("action.add"));
|
builder.setTitle(bundle.getString("action.add"));
|
||||||
builder.removeAllActions();
|
builder.removeAllActions();
|
||||||
builder.addOkAction();
|
builder.addOkAction();
|
||||||
builder.addCancelAction();
|
builder.addCancelAction();
|
||||||
builder.setCenterPanel(rootPanel);
|
builder.setCenterPanel(centerPanel);
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected @Nullable TranslationUpdate handleExit(int exitCode) {
|
||||||
|
if(exitCode == DialogWrapper.OK_EXIT_CODE) {
|
||||||
|
return new TranslationCreate(getState());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
@ -3,102 +3,53 @@ package de.marhali.easyi18n.dialog;
|
|||||||
import com.intellij.openapi.project.Project;
|
import com.intellij.openapi.project.Project;
|
||||||
import com.intellij.openapi.ui.DialogBuilder;
|
import com.intellij.openapi.ui.DialogBuilder;
|
||||||
import com.intellij.openapi.ui.DialogWrapper;
|
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.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.*;
|
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
|
* @author marhali
|
||||||
*/
|
*/
|
||||||
public class EditDialog {
|
public class EditDialog extends TranslationDialog {
|
||||||
|
|
||||||
private final Project project;
|
/**
|
||||||
private final KeyPathConverter converter;
|
* Constructs a new edit dialog with the provided translation
|
||||||
|
* @param project Opened project
|
||||||
private final KeyedTranslation origin;
|
* @param origin Translation to edit
|
||||||
|
*/
|
||||||
private JBTextField keyTextField;
|
public EditDialog(@NotNull Project project, @NotNull Translation origin) {
|
||||||
private Map<String, JBTextField> valueTextFields;
|
super(project, origin);
|
||||||
|
|
||||||
public EditDialog(Project project, KeyedTranslation origin) {
|
|
||||||
this.project = project;
|
|
||||||
this.converter = new KeyPathConverter(project);
|
|
||||||
this.origin = origin;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void showAndHandle() {
|
@Override
|
||||||
int code = prepare().show();
|
protected @NotNull DialogBuilder configure(@NotNull JComponent centerPanel) {
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
DialogBuilder builder = new DialogBuilder();
|
DialogBuilder builder = new DialogBuilder();
|
||||||
builder.setTitle(ResourceBundle.getBundle("messages").getString("action.edit"));
|
builder.setTitle(bundle.getString("action.edit"));
|
||||||
builder.removeAllActions();
|
builder.removeAllActions();
|
||||||
builder.addCancelAction();
|
builder.addCancelAction();
|
||||||
builder.addActionDescriptor(new DeleteActionDescriptor());
|
builder.addActionDescriptor(new DeleteActionDescriptor());
|
||||||
builder.addOkAction();
|
builder.addOkAction();
|
||||||
builder.setCenterPanel(rootPanel);
|
builder.setCenterPanel(centerPanel);
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected @Nullable TranslationUpdate handleExit(int exitCode) {
|
||||||
|
switch (exitCode) {
|
||||||
|
case DialogWrapper.OK_EXIT_CODE:
|
||||||
|
return new TranslationUpdate(origin, getState());
|
||||||
|
case DeleteActionDescriptor.EXIT_CODE:
|
||||||
|
return new TranslationDelete(origin);
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,173 +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
|
|
||||||
*/
|
|
||||||
public class SettingsDialog {
|
|
||||||
|
|
||||||
private final Project project;
|
|
||||||
|
|
||||||
private TextFieldWithBrowseButton pathText;
|
|
||||||
private ComboBox<String> folderStrategyComboBox;
|
|
||||||
private ComboBox<String> 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());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
148
src/main/java/de/marhali/easyi18n/dialog/TranslationDialog.java
Normal file
148
src/main/java/de/marhali/easyi18n/dialog/TranslationDialog.java
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
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.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.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 Map<String, JTextField> localeValueFields;
|
||||||
|
|
||||||
|
private final Set<Consumer<TranslationUpdate>> callbacks;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
this.callbacks = new HashSet<>();
|
||||||
|
|
||||||
|
// Fields
|
||||||
|
TranslationValue value = origin.getValue();
|
||||||
|
|
||||||
|
this.keyField = new JBTextField(converter.toString(origin.getKey()));
|
||||||
|
this.localeValueFields = new HashMap<>();
|
||||||
|
|
||||||
|
for(String locale : InstanceManager.get(project).store().getData().getLocales()) {
|
||||||
|
localeValueFields.put(locale, new JBTextField(value != null ? value.get(locale) : null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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<TranslationUpdate> 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.
|
||||||
|
* @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
|
||||||
|
* @return update conclusion, null if aborted
|
||||||
|
*/
|
||||||
|
protected abstract @Nullable TranslationUpdate 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();
|
||||||
|
TranslationUpdate update = handleExit(exitCode);
|
||||||
|
|
||||||
|
if(update != null) {
|
||||||
|
InstanceManager.get(project).processUpdate(update);
|
||||||
|
callbacks.forEach(callback -> callback.consume(update));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve current modal state.
|
||||||
|
* @return Translation
|
||||||
|
*/
|
||||||
|
protected @NotNull Translation getState() {
|
||||||
|
KeyPath key = converter.fromString(keyField.getText());
|
||||||
|
|
||||||
|
TranslationValue value = new TranslationValue();
|
||||||
|
|
||||||
|
for(Map.Entry<String, JTextField> 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)
|
||||||
|
.addComponent(createLocalesPanel(), 12)
|
||||||
|
.getPanel();
|
||||||
|
|
||||||
|
panel.setMinimumSize(new Dimension(200, 150));
|
||||||
|
|
||||||
|
return configure(panel);
|
||||||
|
}
|
||||||
|
|
||||||
|
private JComponent createLocalesPanel() {
|
||||||
|
FormBuilder builder = FormBuilder.createFormBuilder();
|
||||||
|
|
||||||
|
for(Map.Entry<String, JTextField> 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;
|
||||||
|
}
|
||||||
|
}
|
@ -1,59 +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.SettingsState;
|
|
||||||
import de.marhali.easyi18n.model.TranslationNode;
|
|
||||||
import de.marhali.easyi18n.service.SettingsService;
|
|
||||||
|
|
||||||
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(!SettingsService.getInstance(project).getState().isCodeAssistance()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SettingsState state = SettingsService.getInstance(project).getState();
|
|
||||||
String pathPrefix = state.getPathPrefix();
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,62 +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.service.*;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.*;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* I18n translation key completion provider.
|
|
||||||
* @author marhali
|
|
||||||
*/
|
|
||||||
public class KeyCompletionProvider extends CompletionProvider<CompletionParameters> {
|
|
||||||
|
|
||||||
@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(!SettingsService.getInstance(project).getState().isCodeAssistance()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DataStore store = InstanceManager.get(project).store();
|
|
||||||
|
|
||||||
String previewLocale = SettingsService.getInstance(project).getState().getPreviewLocale();
|
|
||||||
String pathPrefix = SettingsService.getInstance(project).getState().getPathPrefix();
|
|
||||||
|
|
||||||
if(pathPrefix.length() > 0 && !pathPrefix.endsWith(KeyPath.DELIMITER)) {
|
|
||||||
pathPrefix += KeyPath.DELIMITER;
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<KeyPath> 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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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<PsiElement> {
|
|
||||||
|
|
||||||
@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:/]+$");
|
|
||||||
}
|
|
||||||
}
|
|
@ -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.service.SettingsService;
|
|
||||||
|
|
||||||
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<PsiLiteralValue> literalValues = PsiTreeUtil.findChildrenOfType(root, PsiLiteralValue.class);
|
|
||||||
List<FoldingDescriptor> descriptors = new ArrayList<>();
|
|
||||||
|
|
||||||
if(!SettingsService.getInstance(root.getProject()).getState().isCodeAssistance()) {
|
|
||||||
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 = SettingsService.getInstance(literalValue.getProject()).getState().getPreviewLocale();
|
|
||||||
|
|
||||||
return translation.get(previewLocale);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCollapsedByDefault(@NotNull ASTNode node) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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());
|
|
||||||
}
|
|
||||||
}
|
|
@ -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.service.SettingsService;
|
|
||||||
|
|
||||||
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(!SettingsService.getInstance(element.getProject()).getState().isCodeAssistance()) {
|
|
||||||
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) };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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.service.SettingsService;
|
|
||||||
|
|
||||||
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(!SettingsService.getInstance(element.getProject()).getState().isCodeAssistance()) {
|
|
||||||
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) };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
@ -9,6 +9,7 @@ import de.marhali.easyi18n.io.parser.ParserStrategy;
|
|||||||
import de.marhali.easyi18n.io.parser.ParserStrategyType;
|
import de.marhali.easyi18n.io.parser.ParserStrategyType;
|
||||||
import de.marhali.easyi18n.model.*;
|
import de.marhali.easyi18n.model.*;
|
||||||
|
|
||||||
|
import de.marhali.easyi18n.settings.ProjectSettings;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@ -21,23 +22,23 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public class IOHandler {
|
public class IOHandler {
|
||||||
|
|
||||||
private final @NotNull SettingsState settings;
|
private final @NotNull ProjectSettings settings;
|
||||||
|
|
||||||
private final @NotNull FolderStrategy folderStrategy;
|
private final @NotNull FolderStrategy folderStrategy;
|
||||||
|
|
||||||
private final @NotNull ParserStrategyType parserStrategyType;
|
private final @NotNull ParserStrategyType parserStrategyType;
|
||||||
private final @NotNull ParserStrategy parserStrategy;
|
private final @NotNull ParserStrategy parserStrategy;
|
||||||
|
|
||||||
public IOHandler(@NotNull SettingsState settings) throws Exception {
|
public IOHandler(@NotNull ProjectSettings settings) throws Exception {
|
||||||
|
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
|
|
||||||
this.folderStrategy = settings.getFolderStrategy().getStrategy()
|
this.folderStrategy = settings.getFolderStrategy().getStrategy()
|
||||||
.getDeclaredConstructor(SettingsState.class).newInstance(settings);
|
.getDeclaredConstructor(ProjectSettings.class).newInstance(settings);
|
||||||
|
|
||||||
this.parserStrategyType = settings.getParserStrategy();
|
this.parserStrategyType = settings.getParserStrategy();
|
||||||
this.parserStrategy = parserStrategyType.getStrategy()
|
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
|
* @throws IOException Could not read translation data
|
||||||
*/
|
*/
|
||||||
public @NotNull TranslationData read() throws IOException {
|
public @NotNull TranslationData read() throws IOException {
|
||||||
String localesPath = this.settings.getLocalesPath();
|
String localesPath = this.settings.getLocalesDirectory();
|
||||||
|
|
||||||
if(localesPath == null || localesPath.isEmpty()) {
|
if(localesPath == null || localesPath.isEmpty()) {
|
||||||
throw new EmptyLocalesDirException("Locales path must not be empty");
|
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 + ")");
|
throw new IllegalArgumentException("Specified locales path is invalid (" + localesPath + ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
TranslationData data = new TranslationData(this.settings.isSortKeys());
|
TranslationData data = new TranslationData(this.settings.isSorting());
|
||||||
List<TranslationFile> translationFiles = this.folderStrategy.analyzeFolderStructure(localesDirectory);
|
List<TranslationFile> translationFiles = this.folderStrategy.analyzeFolderStructure(localesDirectory);
|
||||||
|
|
||||||
for(TranslationFile file : translationFiles) {
|
for(TranslationFile file : translationFiles) {
|
||||||
@ -80,7 +81,7 @@ public class IOHandler {
|
|||||||
* @throws IOException Write action failed
|
* @throws IOException Write action failed
|
||||||
*/
|
*/
|
||||||
public void write(@NotNull TranslationData data) throws IOException {
|
public void write(@NotNull TranslationData data) throws IOException {
|
||||||
String localesPath = this.settings.getLocalesPath();
|
String localesPath = this.settings.getLocalesDirectory();
|
||||||
|
|
||||||
if(localesPath == null || localesPath.isEmpty()) {
|
if(localesPath == null || localesPath.isEmpty()) {
|
||||||
throw new EmptyLocalesDirException("Locales path must not be empty");
|
throw new EmptyLocalesDirException("Locales path must not be empty");
|
||||||
|
@ -4,10 +4,10 @@ import com.intellij.openapi.vfs.LocalFileSystem;
|
|||||||
import com.intellij.openapi.vfs.VirtualFile;
|
import com.intellij.openapi.vfs.VirtualFile;
|
||||||
|
|
||||||
import de.marhali.easyi18n.io.parser.ParserStrategyType;
|
import de.marhali.easyi18n.io.parser.ParserStrategyType;
|
||||||
import de.marhali.easyi18n.model.SettingsState;
|
|
||||||
import de.marhali.easyi18n.model.TranslationData;
|
import de.marhali.easyi18n.model.TranslationData;
|
||||||
import de.marhali.easyi18n.model.TranslationFile;
|
import de.marhali.easyi18n.model.TranslationFile;
|
||||||
|
|
||||||
|
import de.marhali.easyi18n.settings.ProjectSettings;
|
||||||
import org.apache.commons.io.FilenameUtils;
|
import org.apache.commons.io.FilenameUtils;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
@ -22,9 +22,9 @@ import java.util.Objects;
|
|||||||
*/
|
*/
|
||||||
public abstract class FolderStrategy {
|
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;
|
this.settings = settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,23 +1,24 @@
|
|||||||
package de.marhali.easyi18n.model;
|
package de.marhali.easyi18n.io.folder;
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents all supported folder strategies.
|
* Represents all supported folder strategies.
|
||||||
* @author marhali
|
* @author marhali
|
||||||
*/
|
*/
|
||||||
public enum FolderStrategyType {
|
public enum FolderStrategyType {
|
||||||
SINGLE(SingleFolderStrategy.class),
|
SINGLE(SingleFolderStrategy.class, false),
|
||||||
MODULARIZED_LOCALE(ModularLocaleFolderStrategy.class),
|
MODULARIZED_LOCALE(ModularLocaleFolderStrategy.class, true),
|
||||||
MODULARIZED_NAMESPACE(ModularNamespaceFolderStrategy.class);
|
MODULARIZED_NAMESPACE(ModularNamespaceFolderStrategy.class, true);
|
||||||
|
|
||||||
private final Class<? extends FolderStrategy> strategy;
|
private final Class<? extends FolderStrategy> strategy;
|
||||||
|
private final boolean namespaceMode;
|
||||||
|
|
||||||
FolderStrategyType(Class<? extends FolderStrategy> strategy) {
|
/**
|
||||||
|
* @param strategy Strategy implementation
|
||||||
|
* @param namespaceMode Does this strategy use namespaces?
|
||||||
|
*/
|
||||||
|
FolderStrategyType(Class<? extends FolderStrategy> strategy, boolean namespaceMode) {
|
||||||
this.strategy = strategy;
|
this.strategy = strategy;
|
||||||
|
this.namespaceMode = namespaceMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Class<? extends FolderStrategy> getStrategy() {
|
public Class<? extends FolderStrategy> getStrategy() {
|
||||||
@ -38,6 +39,10 @@ public enum FolderStrategyType {
|
|||||||
throw new NullPointerException();
|
throw new NullPointerException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isNamespaceMode() {
|
||||||
|
return namespaceMode;
|
||||||
|
}
|
||||||
|
|
||||||
public static FolderStrategyType fromIndex(int index) {
|
public static FolderStrategyType fromIndex(int index) {
|
||||||
return values()[index];
|
return values()[index];
|
||||||
}
|
}
|
@ -3,9 +3,9 @@ package de.marhali.easyi18n.io.folder;
|
|||||||
import com.intellij.openapi.vfs.VirtualFile;
|
import com.intellij.openapi.vfs.VirtualFile;
|
||||||
|
|
||||||
import de.marhali.easyi18n.io.parser.ParserStrategyType;
|
import de.marhali.easyi18n.io.parser.ParserStrategyType;
|
||||||
import de.marhali.easyi18n.model.SettingsState;
|
|
||||||
import de.marhali.easyi18n.model.TranslationData;
|
import de.marhali.easyi18n.model.TranslationData;
|
||||||
import de.marhali.easyi18n.model.TranslationFile;
|
import de.marhali.easyi18n.model.TranslationFile;
|
||||||
|
import de.marhali.easyi18n.settings.ProjectSettings;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
@ -20,7 +20,7 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public class ModularLocaleFolderStrategy extends FolderStrategy {
|
public class ModularLocaleFolderStrategy extends FolderStrategy {
|
||||||
|
|
||||||
public ModularLocaleFolderStrategy(@NotNull SettingsState settings) {
|
public ModularLocaleFolderStrategy(@NotNull ProjectSettings settings) {
|
||||||
super(settings);
|
super(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,9 +3,9 @@ package de.marhali.easyi18n.io.folder;
|
|||||||
import com.intellij.openapi.vfs.VirtualFile;
|
import com.intellij.openapi.vfs.VirtualFile;
|
||||||
|
|
||||||
import de.marhali.easyi18n.io.parser.ParserStrategyType;
|
import de.marhali.easyi18n.io.parser.ParserStrategyType;
|
||||||
import de.marhali.easyi18n.model.SettingsState;
|
|
||||||
import de.marhali.easyi18n.model.TranslationData;
|
import de.marhali.easyi18n.model.TranslationData;
|
||||||
import de.marhali.easyi18n.model.TranslationFile;
|
import de.marhali.easyi18n.model.TranslationFile;
|
||||||
|
import de.marhali.easyi18n.settings.ProjectSettings;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
@ -20,7 +20,7 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public class ModularNamespaceFolderStrategy extends FolderStrategy {
|
public class ModularNamespaceFolderStrategy extends FolderStrategy {
|
||||||
|
|
||||||
public ModularNamespaceFolderStrategy(@NotNull SettingsState settings) {
|
public ModularNamespaceFolderStrategy(@NotNull ProjectSettings settings) {
|
||||||
super(settings);
|
super(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,9 +3,9 @@ package de.marhali.easyi18n.io.folder;
|
|||||||
import com.intellij.openapi.vfs.VirtualFile;
|
import com.intellij.openapi.vfs.VirtualFile;
|
||||||
|
|
||||||
import de.marhali.easyi18n.io.parser.ParserStrategyType;
|
import de.marhali.easyi18n.io.parser.ParserStrategyType;
|
||||||
import de.marhali.easyi18n.model.SettingsState;
|
|
||||||
import de.marhali.easyi18n.model.TranslationData;
|
import de.marhali.easyi18n.model.TranslationData;
|
||||||
import de.marhali.easyi18n.model.TranslationFile;
|
import de.marhali.easyi18n.model.TranslationFile;
|
||||||
|
import de.marhali.easyi18n.settings.ProjectSettings;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public class SingleFolderStrategy extends FolderStrategy {
|
public class SingleFolderStrategy extends FolderStrategy {
|
||||||
|
|
||||||
public SingleFolderStrategy(@NotNull SettingsState settings) {
|
public SingleFolderStrategy(@NotNull ProjectSettings settings) {
|
||||||
super(settings);
|
super(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,8 @@ package de.marhali.easyi18n.io.parser;
|
|||||||
|
|
||||||
import de.marhali.easyi18n.model.*;
|
import de.marhali.easyi18n.model.*;
|
||||||
|
|
||||||
|
import de.marhali.easyi18n.model.KeyPath;
|
||||||
|
import de.marhali.easyi18n.settings.ProjectSettings;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@ -12,9 +14,9 @@ import java.util.Objects;
|
|||||||
*/
|
*/
|
||||||
public abstract class ParserStrategy {
|
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;
|
this.settings = settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,10 +47,10 @@ public abstract class ParserStrategy {
|
|||||||
|
|
||||||
if(file.getNamespace() != null) {
|
if(file.getNamespace() != null) {
|
||||||
String moduleName = file.getNamespace();
|
String moduleName = file.getNamespace();
|
||||||
TranslationNode moduleNode = data.getNode(KeyPath.of(moduleName));
|
TranslationNode moduleNode = data.getNode(new KeyPath(moduleName));
|
||||||
|
|
||||||
if(moduleNode == null) {
|
if(moduleNode == null) {
|
||||||
moduleNode = new TranslationNode(this.settings.isSortKeys());
|
moduleNode = new TranslationNode(this.settings.isSorting());
|
||||||
data.getRootNode().setChildren(moduleName, moduleNode);
|
data.getRootNode().setChildren(moduleName, moduleNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,7 +70,7 @@ public abstract class ParserStrategy {
|
|||||||
TranslationNode targetNode = data.getRootNode();
|
TranslationNode targetNode = data.getRootNode();
|
||||||
|
|
||||||
if(file.getNamespace() != null) {
|
if(file.getNamespace() != null) {
|
||||||
targetNode = data.getNode(KeyPath.of(file.getNamespace()));
|
targetNode = data.getNode(new KeyPath(file.getNamespace()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Objects.requireNonNull(targetNode);
|
return Objects.requireNonNull(targetNode);
|
||||||
|
@ -4,8 +4,8 @@ import com.google.gson.JsonElement;
|
|||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import com.google.gson.JsonPrimitive;
|
import com.google.gson.JsonPrimitive;
|
||||||
|
|
||||||
import de.marhali.easyi18n.model.Translation;
|
|
||||||
import de.marhali.easyi18n.model.TranslationNode;
|
import de.marhali.easyi18n.model.TranslationNode;
|
||||||
|
import de.marhali.easyi18n.model.TranslationValue;
|
||||||
import de.marhali.easyi18n.util.StringUtil;
|
import de.marhali.easyi18n.util.StringUtil;
|
||||||
|
|
||||||
import org.apache.commons.lang.StringEscapeUtils;
|
import org.apache.commons.lang.StringEscapeUtils;
|
||||||
@ -30,7 +30,7 @@ public class JsonMapper {
|
|||||||
// Nested element - run recursively
|
// Nested element - run recursively
|
||||||
read(locale, value.getAsJsonObject(), childNode);
|
read(locale, value.getAsJsonObject(), childNode);
|
||||||
} else {
|
} else {
|
||||||
Translation translation = childNode.getValue();
|
TranslationValue translation = childNode.getValue();
|
||||||
|
|
||||||
String content = entry.getValue().isJsonArray()
|
String content = entry.getValue().isJsonArray()
|
||||||
? JsonArrayMapper.read(value.getAsJsonArray())
|
? JsonArrayMapper.read(value.getAsJsonArray())
|
||||||
@ -55,7 +55,7 @@ public class JsonMapper {
|
|||||||
json.add(key, childJson);
|
json.add(key, childJson);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Translation translation = childNode.getValue();
|
TranslationValue translation = childNode.getValue();
|
||||||
String content = translation.get(locale);
|
String content = translation.get(locale);
|
||||||
|
|
||||||
if(content != null) {
|
if(content != null) {
|
||||||
|
@ -3,14 +3,15 @@ package de.marhali.easyi18n.io.parser.json;
|
|||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.GsonBuilder;
|
import com.google.gson.GsonBuilder;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
|
|
||||||
import com.intellij.openapi.vfs.VirtualFile;
|
import com.intellij.openapi.vfs.VirtualFile;
|
||||||
|
|
||||||
import de.marhali.easyi18n.io.parser.ParserStrategy;
|
import de.marhali.easyi18n.io.parser.ParserStrategy;
|
||||||
import de.marhali.easyi18n.model.*;
|
import de.marhali.easyi18n.model.*;
|
||||||
|
import de.marhali.easyi18n.settings.ProjectSettings;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@ -23,7 +24,7 @@ public class JsonParserStrategy extends ParserStrategy {
|
|||||||
|
|
||||||
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
|
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
|
||||||
|
|
||||||
public JsonParserStrategy(@NotNull SettingsState settings) {
|
public JsonParserStrategy(@NotNull ProjectSettings settings) {
|
||||||
super(settings);
|
super(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
package de.marhali.easyi18n.io.parser.json5;
|
package de.marhali.easyi18n.io.parser.json5;
|
||||||
|
|
||||||
import de.marhali.easyi18n.model.Translation;
|
|
||||||
import de.marhali.easyi18n.model.TranslationNode;
|
import de.marhali.easyi18n.model.TranslationNode;
|
||||||
|
import de.marhali.easyi18n.model.TranslationValue;
|
||||||
import de.marhali.easyi18n.util.StringUtil;
|
import de.marhali.easyi18n.util.StringUtil;
|
||||||
|
|
||||||
import de.marhali.json5.Json5Element;
|
import de.marhali.json5.Json5Element;
|
||||||
import de.marhali.json5.Json5Object;
|
import de.marhali.json5.Json5Object;
|
||||||
import de.marhali.json5.Json5Primitive;
|
import de.marhali.json5.Json5Primitive;
|
||||||
|
|
||||||
import org.apache.commons.lang.StringEscapeUtils;
|
import org.apache.commons.lang.StringEscapeUtils;
|
||||||
import org.apache.commons.lang.math.NumberUtils;
|
import org.apache.commons.lang.math.NumberUtils;
|
||||||
|
|
||||||
@ -28,7 +28,7 @@ public class Json5Mapper {
|
|||||||
// Nested element - run recursively
|
// Nested element - run recursively
|
||||||
read(locale, value.getAsJson5Object(), childNode);
|
read(locale, value.getAsJson5Object(), childNode);
|
||||||
} else {
|
} else {
|
||||||
Translation translation = childNode.getValue();
|
TranslationValue translation = childNode.getValue();
|
||||||
|
|
||||||
String content = value.isJson5Array()
|
String content = value.isJson5Array()
|
||||||
? Json5ArrayMapper.read(value.getAsJson5Array())
|
? Json5ArrayMapper.read(value.getAsJson5Array())
|
||||||
@ -54,7 +54,7 @@ public class Json5Mapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Translation translation = childNode.getValue();
|
TranslationValue translation = childNode.getValue();
|
||||||
String content = translation.get(locale);
|
String content = translation.get(locale);
|
||||||
if(content != null) {
|
if(content != null) {
|
||||||
if(Json5ArrayMapper.isArray(content)) {
|
if(Json5ArrayMapper.isArray(content)) {
|
||||||
|
@ -3,10 +3,10 @@ package de.marhali.easyi18n.io.parser.json5;
|
|||||||
import com.intellij.openapi.vfs.VirtualFile;
|
import com.intellij.openapi.vfs.VirtualFile;
|
||||||
|
|
||||||
import de.marhali.easyi18n.io.parser.ParserStrategy;
|
import de.marhali.easyi18n.io.parser.ParserStrategy;
|
||||||
import de.marhali.easyi18n.model.SettingsState;
|
|
||||||
import de.marhali.easyi18n.model.TranslationData;
|
import de.marhali.easyi18n.model.TranslationData;
|
||||||
import de.marhali.easyi18n.model.TranslationFile;
|
import de.marhali.easyi18n.model.TranslationFile;
|
||||||
import de.marhali.easyi18n.model.TranslationNode;
|
import de.marhali.easyi18n.model.TranslationNode;
|
||||||
|
import de.marhali.easyi18n.settings.ProjectSettings;
|
||||||
import de.marhali.json5.Json5;
|
import de.marhali.json5.Json5;
|
||||||
import de.marhali.json5.Json5Element;
|
import de.marhali.json5.Json5Element;
|
||||||
import de.marhali.json5.Json5Object;
|
import de.marhali.json5.Json5Object;
|
||||||
@ -26,7 +26,7 @@ public class Json5ParserStrategy extends ParserStrategy {
|
|||||||
private static final Json5 JSON5 = Json5.builder(builder ->
|
private static final Json5 JSON5 = Json5.builder(builder ->
|
||||||
builder.allowInvalidSurrogate().trailingComma().indentFactor(4).build());
|
builder.allowInvalidSurrogate().trailingComma().indentFactor(4).build());
|
||||||
|
|
||||||
public Json5ParserStrategy(@NotNull SettingsState settings) {
|
public Json5ParserStrategy(@NotNull ProjectSettings settings) {
|
||||||
super(settings);
|
super(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
package de.marhali.easyi18n.io.parser.properties;
|
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.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 de.marhali.easyi18n.util.StringUtil;
|
||||||
|
|
||||||
import org.apache.commons.lang.StringEscapeUtils;
|
import org.apache.commons.lang.StringEscapeUtils;
|
||||||
@ -16,15 +17,17 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
public class PropertiesMapper {
|
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<Object, Object> entry : properties.entrySet()) {
|
for(Map.Entry<Object, Object> entry : properties.entrySet()) {
|
||||||
KeyPath key = new KeyPath(String.valueOf(entry.getKey()));
|
KeyPath key = converter.fromString(String.valueOf(entry.getKey()));
|
||||||
Object value = entry.getValue();
|
Object value = entry.getValue();
|
||||||
|
|
||||||
Translation translation = data.getTranslation(key);
|
TranslationValue translation = data.getTranslation(key);
|
||||||
|
|
||||||
if(translation == null) {
|
if(translation == null) {
|
||||||
translation = new Translation();
|
translation = new TranslationValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
String content = value instanceof String[]
|
String content = value instanceof String[]
|
||||||
@ -36,12 +39,14 @@ public class PropertiesMapper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void write(String locale, SortableProperties properties, TranslationData data) {
|
public static void write(String locale, SortableProperties properties,
|
||||||
for(KeyPath key : data.getFullKeys()) {
|
TranslationData data, KeyPathConverter converter) {
|
||||||
Translation translation = data.getTranslation(key);
|
|
||||||
|
|
||||||
if(translation != null && translation.containsKey(locale)) {
|
for(KeyPath key : data.getFullKeys()) {
|
||||||
String simpleKey = key.toSimpleString();
|
TranslationValue translation = data.getTranslation(key);
|
||||||
|
|
||||||
|
if(translation != null && translation.containsLocale(locale)) {
|
||||||
|
String simpleKey = converter.toString(key);
|
||||||
String content = translation.get(locale);
|
String content = translation.get(locale);
|
||||||
|
|
||||||
if(PropertiesArrayMapper.isArray(content)) {
|
if(PropertiesArrayMapper.isArray(content)) {
|
||||||
|
@ -3,10 +3,11 @@ package de.marhali.easyi18n.io.parser.properties;
|
|||||||
import com.intellij.openapi.vfs.VirtualFile;
|
import com.intellij.openapi.vfs.VirtualFile;
|
||||||
|
|
||||||
import de.marhali.easyi18n.io.parser.ParserStrategy;
|
import de.marhali.easyi18n.io.parser.ParserStrategy;
|
||||||
import de.marhali.easyi18n.model.SettingsState;
|
|
||||||
import de.marhali.easyi18n.model.TranslationData;
|
import de.marhali.easyi18n.model.TranslationData;
|
||||||
import de.marhali.easyi18n.model.TranslationFile;
|
import de.marhali.easyi18n.model.TranslationFile;
|
||||||
import de.marhali.easyi18n.model.TranslationNode;
|
import de.marhali.easyi18n.model.TranslationNode;
|
||||||
|
import de.marhali.easyi18n.settings.ProjectSettings;
|
||||||
|
import de.marhali.easyi18n.util.KeyPathConverter;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
@ -20,8 +21,11 @@ import java.io.StringWriter;
|
|||||||
*/
|
*/
|
||||||
public class PropertiesParserStrategy extends ParserStrategy {
|
public class PropertiesParserStrategy extends ParserStrategy {
|
||||||
|
|
||||||
public PropertiesParserStrategy(@NotNull SettingsState settings) {
|
private final @NotNull KeyPathConverter converter;
|
||||||
|
|
||||||
|
public PropertiesParserStrategy(@NotNull ProjectSettings settings) {
|
||||||
super(settings);
|
super(settings);
|
||||||
|
this.converter = new KeyPathConverter(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -33,9 +37,9 @@ public class PropertiesParserStrategy extends ParserStrategy {
|
|||||||
TranslationData targetData = new TranslationData(data.getLocales(), targetNode);
|
TranslationData targetData = new TranslationData(data.getLocales(), targetNode);
|
||||||
|
|
||||||
try(Reader reader = new InputStreamReader(vf.getInputStream(), vf.getCharset())) {
|
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);
|
input.load(reader);
|
||||||
PropertiesMapper.read(file.getLocale(), input, targetData);
|
PropertiesMapper.read(file.getLocale(), input, targetData, converter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,8 +48,8 @@ public class PropertiesParserStrategy extends ParserStrategy {
|
|||||||
TranslationNode targetNode = super.getTargetNode(data, file);
|
TranslationNode targetNode = super.getTargetNode(data, file);
|
||||||
TranslationData targetData = new TranslationData(data.getLocales(), targetNode);
|
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);
|
PropertiesMapper.write(file.getLocale(), output, targetData, converter);
|
||||||
|
|
||||||
try(StringWriter writer = new StringWriter()) {
|
try(StringWriter writer = new StringWriter()) {
|
||||||
output.store(writer, null);
|
output.store(writer, null);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package de.marhali.easyi18n.io.parser.yaml;
|
package de.marhali.easyi18n.io.parser.yaml;
|
||||||
|
|
||||||
import de.marhali.easyi18n.model.Translation;
|
|
||||||
import de.marhali.easyi18n.model.TranslationNode;
|
import de.marhali.easyi18n.model.TranslationNode;
|
||||||
|
import de.marhali.easyi18n.model.TranslationValue;
|
||||||
import de.marhali.easyi18n.util.StringUtil;
|
import de.marhali.easyi18n.util.StringUtil;
|
||||||
|
|
||||||
import org.apache.commons.lang.StringEscapeUtils;
|
import org.apache.commons.lang.StringEscapeUtils;
|
||||||
@ -28,7 +28,7 @@ public class YamlMapper {
|
|||||||
// Nested element - run recursively
|
// Nested element - run recursively
|
||||||
read(locale, (MapSection) value, childNode);
|
read(locale, (MapSection) value, childNode);
|
||||||
} else {
|
} else {
|
||||||
Translation translation = childNode.getValue();
|
TranslationValue translation = childNode.getValue();
|
||||||
|
|
||||||
String content = value instanceof ListSection
|
String content = value instanceof ListSection
|
||||||
? YamlArrayMapper.read((ListSection) value)
|
? YamlArrayMapper.read((ListSection) value)
|
||||||
@ -53,7 +53,7 @@ public class YamlMapper {
|
|||||||
section.setInScope(key, childSection);
|
section.setInScope(key, childSection);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Translation translation = childNode.getValue();
|
TranslationValue translation = childNode.getValue();
|
||||||
String content = translation.get(locale);
|
String content = translation.get(locale);
|
||||||
|
|
||||||
if(content != null) {
|
if(content != null) {
|
||||||
|
@ -3,10 +3,10 @@ package de.marhali.easyi18n.io.parser.yaml;
|
|||||||
import com.intellij.openapi.vfs.VirtualFile;
|
import com.intellij.openapi.vfs.VirtualFile;
|
||||||
|
|
||||||
import de.marhali.easyi18n.io.parser.ParserStrategy;
|
import de.marhali.easyi18n.io.parser.ParserStrategy;
|
||||||
import de.marhali.easyi18n.model.SettingsState;
|
|
||||||
import de.marhali.easyi18n.model.TranslationData;
|
import de.marhali.easyi18n.model.TranslationData;
|
||||||
import de.marhali.easyi18n.model.TranslationFile;
|
import de.marhali.easyi18n.model.TranslationFile;
|
||||||
import de.marhali.easyi18n.model.TranslationNode;
|
import de.marhali.easyi18n.model.TranslationNode;
|
||||||
|
import de.marhali.easyi18n.settings.ProjectSettings;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ import java.io.Reader;
|
|||||||
*/
|
*/
|
||||||
public class YamlParserStrategy extends ParserStrategy {
|
public class YamlParserStrategy extends ParserStrategy {
|
||||||
|
|
||||||
public YamlParserStrategy(@NotNull SettingsState settings) {
|
public YamlParserStrategy(@NotNull ProjectSettings settings) {
|
||||||
super(settings);
|
super(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,58 +1,37 @@
|
|||||||
package de.marhali.easyi18n.model;
|
package de.marhali.easyi18n.model;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a full translation key with all sections.
|
* Represents the absolute key path for a desired translation.
|
||||||
* Implementations can use single section or variable section length variants.
|
* The key could be based one or many sections.
|
||||||
* The respective layer (io, presentation) is responsible for using the correct mapping mechanism.
|
* Classes implementing this structure need to take care on how to layer translations paths.
|
||||||
* @author marhali
|
* @author marhali
|
||||||
*/
|
*/
|
||||||
public class KeyPath extends ArrayList<String> {
|
public class KeyPath extends ArrayList<String> {
|
||||||
|
|
||||||
public static final String DELIMITER = ".";
|
public KeyPath() {}
|
||||||
|
|
||||||
public static KeyPath of(@NotNull String... path) {
|
public KeyPath(@Nullable String... path) {
|
||||||
return new KeyPath(List.of(path));
|
super.addAll(List.of(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
public KeyPath() {
|
public KeyPath(@NotNull List<String> path) {
|
||||||
super();
|
super(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
public KeyPath(@NotNull KeyPath path, String... pathToAppend) {
|
public KeyPath(@NotNull KeyPath path, @Nullable String... pathToAppend) {
|
||||||
this(path);
|
this(path);
|
||||||
this.addAll(List.of(pathToAppend));
|
super.addAll(List.of(pathToAppend));
|
||||||
}
|
}
|
||||||
|
|
||||||
public KeyPath(@NotNull Collection<? extends String> c) {
|
@Override
|
||||||
super(c);
|
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))));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <b>Note: </b>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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,69 +0,0 @@
|
|||||||
package de.marhali.easyi18n.model;
|
|
||||||
|
|
||||||
import com.intellij.openapi.project.Project;
|
|
||||||
|
|
||||||
import de.marhali.easyi18n.service.SettingsService;
|
|
||||||
|
|
||||||
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
|
|
||||||
*/
|
|
||||||
public class KeyPathConverter {
|
|
||||||
|
|
||||||
private final boolean nestKeys;
|
|
||||||
|
|
||||||
public KeyPathConverter(boolean nestKeys) {
|
|
||||||
this.nestKeys = nestKeys;
|
|
||||||
}
|
|
||||||
|
|
||||||
public KeyPathConverter(@NotNull Project project) {
|
|
||||||
this(SettingsService.getInstance(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 ?
|
|
||||||
"(?<!\\\\)" + Pattern.quote(KeyPath.DELIMITER) : Pattern.quote("\\\\" + KeyPath.DELIMITER));
|
|
||||||
|
|
||||||
KeyPath path = new KeyPath();
|
|
||||||
|
|
||||||
for(String section : sections) {
|
|
||||||
path.add(section.replace("\\\\" + KeyPath.DELIMITER, KeyPath.DELIMITER));
|
|
||||||
}
|
|
||||||
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "KeyPathConverter{" +
|
|
||||||
"nestKeys=" + nestKeys +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,43 +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
|
|
||||||
*/
|
|
||||||
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 +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,105 +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
|
|
||||||
*/
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,29 +1,54 @@
|
|||||||
package de.marhali.easyi18n.model;
|
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.
|
* Represents a translation with defined key and locale values.
|
||||||
* This class contains only the translations for this unspecific element.
|
*
|
||||||
* @author marhali
|
* @author marhali
|
||||||
*/
|
*/
|
||||||
public class Translation extends HashMap<String, String> {
|
public class Translation {
|
||||||
public Translation() {
|
|
||||||
super();
|
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();
|
* @return Absolute key path
|
||||||
super.put(locale, content);
|
*/
|
||||||
|
public @NotNull KeyPath getKey() {
|
||||||
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Translation add(String locale, String content) {
|
/**
|
||||||
super.put(locale, content);
|
* @return values - nullable to indicate removal
|
||||||
return this;
|
*/
|
||||||
|
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
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return super.toString();
|
return "Translation{" +
|
||||||
|
"key=" + key +
|
||||||
|
", value=" + value +
|
||||||
|
'}';
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -93,7 +93,7 @@ public class TranslationData {
|
|||||||
* @param fullPath Absolute translation key path
|
* @param fullPath Absolute translation key path
|
||||||
* @return Found translation. Can be null if path is empty or is not a leaf element
|
* @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);
|
TranslationNode node = this.getNode(fullPath);
|
||||||
|
|
||||||
if(node == null || !node.isLeaf()) {
|
if(node == null || !node.isLeaf()) {
|
||||||
@ -109,7 +109,7 @@ public class TranslationData {
|
|||||||
* @param fullPath Absolute translation key path
|
* @param fullPath Absolute translation key path
|
||||||
* @param translation Translation to set. Can be null to delete the corresponding node
|
* @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()) {
|
if(fullPath.isEmpty()) {
|
||||||
throw new IllegalArgumentException("Key path cannot be empty");
|
throw new IllegalArgumentException("Key path cannot be empty");
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ public class TranslationNode {
|
|||||||
private Map<String, TranslationNode> children;
|
private Map<String, TranslationNode> children;
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private Translation value;
|
private TranslationValue value;
|
||||||
|
|
||||||
public TranslationNode(boolean sort) {
|
public TranslationNode(boolean sort) {
|
||||||
this(sort ? new TreeMap<>() : new LinkedHashMap<>());
|
this(sort ? new TreeMap<>() : new LinkedHashMap<>());
|
||||||
@ -40,7 +40,7 @@ public class TranslationNode {
|
|||||||
public TranslationNode(@NotNull Map<String, TranslationNode> children) {
|
public TranslationNode(@NotNull Map<String, TranslationNode> children) {
|
||||||
this.parent = null;
|
this.parent = null;
|
||||||
this.children = children;
|
this.children = children;
|
||||||
this.value = new Translation();
|
this.value = new TranslationValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -62,11 +62,11 @@ public class TranslationNode {
|
|||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NotNull Translation getValue() {
|
public @NotNull TranslationValue getValue() {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setValue(@NotNull Translation value) {
|
public void setValue(@NotNull TranslationValue value) {
|
||||||
this.children.clear();
|
this.children.clear();
|
||||||
this.value = value;
|
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);
|
this.setChildren(key).setValue(translation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,70 @@
|
|||||||
|
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;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the set values behind a specific translation.
|
||||||
|
* @author marhali
|
||||||
|
*/
|
||||||
|
public class TranslationValue {
|
||||||
|
|
||||||
|
private @NotNull Map<String, String> localeValues;
|
||||||
|
|
||||||
|
public TranslationValue() {
|
||||||
|
this.localeValues = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TranslationValue(@NotNull String locale, @NotNull String content) {
|
||||||
|
this();
|
||||||
|
localeValues.put(locale, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Map.Entry<String, String>> getEntries() {
|
||||||
|
return this.localeValues.entrySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<String> getLocaleContents() {
|
||||||
|
return this.localeValues.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLocaleValues(@NotNull Map<String, String> localeValues) {
|
||||||
|
this.localeValues = localeValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable String get(@NotNull String locale) {
|
||||||
|
return this.localeValues.get(locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void put(@NotNull String locale, @NotNull String content) {
|
||||||
|
this.localeValues.put(locale, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 this.localeValues.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
this.localeValues.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "TranslationValue{" +
|
||||||
|
"localeValues=" + localeValues +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
@ -7,7 +9,7 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
* @author marhali
|
* @author marhali
|
||||||
*/
|
*/
|
||||||
public class TranslationCreate extends TranslationUpdate {
|
public class TranslationCreate extends TranslationUpdate {
|
||||||
public TranslationCreate(@NotNull KeyedTranslation translation) {
|
public TranslationCreate(@NotNull Translation translation) {
|
||||||
super(null, translation);
|
super(null, translation);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
@ -7,7 +9,7 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
* @author marhali
|
* @author marhali
|
||||||
*/
|
*/
|
||||||
public class TranslationDelete extends TranslationUpdate {
|
public class TranslationDelete extends TranslationUpdate {
|
||||||
public TranslationDelete(@NotNull KeyedTranslation translation) {
|
public TranslationDelete(@NotNull Translation translation) {
|
||||||
super(translation, null);
|
super(translation, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -10,19 +11,19 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
*/
|
*/
|
||||||
public class TranslationUpdate {
|
public class TranslationUpdate {
|
||||||
|
|
||||||
private final @Nullable KeyedTranslation origin;
|
private final @Nullable Translation origin;
|
||||||
private final @Nullable KeyedTranslation change;
|
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.origin = origin;
|
||||||
this.change = change;
|
this.change = change;
|
||||||
}
|
}
|
||||||
|
|
||||||
public @Nullable KeyedTranslation getOrigin() {
|
public @Nullable Translation getOrigin() {
|
||||||
return origin;
|
return origin;
|
||||||
}
|
}
|
||||||
|
|
||||||
public @Nullable KeyedTranslation getChange() {
|
public @Nullable Translation getChange() {
|
||||||
return change;
|
return change;
|
||||||
}
|
}
|
||||||
|
|
@ -1,37 +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")
|
|
||||||
public class SettingsService implements PersistentStateComponent<SettingsState> {
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void loadState(@NotNull SettingsState state) {
|
|
||||||
this.state = state;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,35 @@
|
|||||||
|
package de.marhali.easyi18n.settings;
|
||||||
|
|
||||||
|
import de.marhali.easyi18n.io.parser.ParserStrategyType;
|
||||||
|
import de.marhali.easyi18n.io.folder.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();
|
||||||
|
|
||||||
|
// Experimental Configuration
|
||||||
|
boolean isAlwaysFold();
|
||||||
|
}
|
@ -0,0 +1,228 @@
|
|||||||
|
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())
|
||||||
|
.addVerticalGap(24)
|
||||||
|
.addComponent(new TitledSeparator(bundle.getString("settings.experimental.title")))
|
||||||
|
.addComponent(constructAlwaysFoldField())
|
||||||
|
.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(createBoldLabel(bundle.getString("settings.editor.key.leaf.title")));
|
||||||
|
panel.add(contextDelimiter = createDelimiterField(bundle.getString("settings.editor.key.context.tooltip")));
|
||||||
|
panel.add(createBoldLabel(bundle.getString("settings.editor.key.context.title")));
|
||||||
|
panel.add(pluralDelimiter = createDelimiterField(bundle.getString("settings.editor.key.plural.tooltip")));
|
||||||
|
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);
|
||||||
|
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 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) {
|
||||||
|
// Automatically suggest file pattern option on parser change
|
||||||
|
ParserStrategyType newStrategy = ParserStrategyType.fromIndex(parserStrategy.getSelectedIndex());
|
||||||
|
filePattern.setText(newStrategy.getExampleFilePattern());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public JPanel getMainPanel() {
|
||||||
|
return mainPanel;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,89 @@
|
|||||||
|
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.io.folder.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> preset;
|
||||||
|
|
||||||
|
// Resource Configuration
|
||||||
|
protected TextFieldWithBrowseButton localesDirectory;
|
||||||
|
protected ComboBox<String> folderStrategy;
|
||||||
|
protected ComboBox<String> 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;
|
||||||
|
|
||||||
|
// Experimental configuration
|
||||||
|
protected JCheckBox alwaysFold;
|
||||||
|
|
||||||
|
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());
|
||||||
|
|
||||||
|
state.setAlwaysFold(alwaysFold.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());
|
||||||
|
|
||||||
|
alwaysFold.setSelected(state.isAlwaysFold());
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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<ProjectSettingsState> {
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,168 @@
|
|||||||
|
package de.marhali.easyi18n.settings;
|
||||||
|
|
||||||
|
import de.marhali.easyi18n.io.parser.ParserStrategyType;
|
||||||
|
import de.marhali.easyi18n.io.folder.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;
|
||||||
|
|
||||||
|
// Experimental configuration
|
||||||
|
private Boolean alwaysFold;
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAlwaysFold() {
|
||||||
|
return alwaysFold != null ? alwaysFold : defaults.isAlwaysFold();
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAlwaysFold(Boolean alwaysFold) {
|
||||||
|
this.alwaysFold = alwaysFold;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
package de.marhali.easyi18n.settings.presets;
|
||||||
|
|
||||||
|
import de.marhali.easyi18n.io.parser.ParserStrategyType;
|
||||||
|
import de.marhali.easyi18n.io.folder.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAlwaysFold() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -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<? extends ProjectSettings> clazz;
|
||||||
|
|
||||||
|
Preset(Class<? extends ProjectSettings> 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();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
package de.marhali.easyi18n.settings.presets;
|
||||||
|
|
||||||
|
import de.marhali.easyi18n.io.parser.ParserStrategyType;
|
||||||
|
import de.marhali.easyi18n.io.folder.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAlwaysFold() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,83 @@
|
|||||||
|
package de.marhali.easyi18n.settings.presets;
|
||||||
|
|
||||||
|
import de.marhali.easyi18n.io.parser.ParserStrategyType;
|
||||||
|
import de.marhali.easyi18n.io.folder.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAlwaysFold() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -5,14 +5,19 @@ import com.intellij.ui.components.JBScrollPane;
|
|||||||
import com.intellij.ui.table.JBTable;
|
import com.intellij.ui.table.JBTable;
|
||||||
|
|
||||||
import de.marhali.easyi18n.InstanceManager;
|
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.dialog.EditDialog;
|
||||||
|
import de.marhali.easyi18n.listener.ReturnKeyListener;
|
||||||
import de.marhali.easyi18n.listener.DeleteKeyListener;
|
import de.marhali.easyi18n.listener.DeleteKeyListener;
|
||||||
import de.marhali.easyi18n.listener.PopupClickListener;
|
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.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.renderer.TableRenderer;
|
||||||
import de.marhali.easyi18n.tabs.mapper.TableModelMapper;
|
import de.marhali.easyi18n.tabs.mapper.TableModelMapper;
|
||||||
|
import de.marhali.easyi18n.util.KeyPathConverter;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
@ -56,20 +61,20 @@ public class TableView implements BusListener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyPath fullPath = this.converter.split(String.valueOf(this.table.getValueAt(row, 0)));
|
KeyPath fullPath = this.converter.fromString(String.valueOf(this.table.getValueAt(row, 0)));
|
||||||
Translation translation = InstanceManager.get(project).store().getData().getTranslation(fullPath);
|
TranslationValue value = InstanceManager.get(project).store().getData().getTranslation(fullPath);
|
||||||
|
|
||||||
if (translation != null) {
|
if (value != null) {
|
||||||
new EditDialog(project, new KeyedTranslation(fullPath, translation)).showAndHandle();
|
new EditDialog(project, new Translation(fullPath, value)).showAndHandle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void deleteSelectedRows() {
|
private void deleteSelectedRows() {
|
||||||
for (int selectedRow : table.getSelectedRows()) {
|
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(
|
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
|
@Override
|
||||||
public void onFocusKey(@NotNull KeyPath key) {
|
public void onFocusKey(@NotNull KeyPath key) {
|
||||||
String concatKey = this.converter.concat(key);
|
String concatKey = this.converter.toString(key);
|
||||||
int row = -1;
|
int row = -1;
|
||||||
|
|
||||||
for (int i = 0; i < table.getRowCount(); i++) {
|
for (int i = 0; i < table.getRowCount(); i++) {
|
||||||
|
@ -9,16 +9,20 @@ import com.intellij.ui.components.JBScrollPane;
|
|||||||
import com.intellij.ui.treeStructure.Tree;
|
import com.intellij.ui.treeStructure.Tree;
|
||||||
|
|
||||||
import de.marhali.easyi18n.InstanceManager;
|
import de.marhali.easyi18n.InstanceManager;
|
||||||
|
import de.marhali.easyi18n.dialog.EditDialog;
|
||||||
import de.marhali.easyi18n.listener.ReturnKeyListener;
|
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.model.bus.BusListener;
|
||||||
import de.marhali.easyi18n.action.treeview.CollapseTreeViewAction;
|
import de.marhali.easyi18n.action.treeview.CollapseTreeViewAction;
|
||||||
import de.marhali.easyi18n.action.treeview.ExpandTreeViewAction;
|
import de.marhali.easyi18n.action.treeview.ExpandTreeViewAction;
|
||||||
import de.marhali.easyi18n.dialog.EditDialog;
|
|
||||||
import de.marhali.easyi18n.listener.DeleteKeyListener;
|
import de.marhali.easyi18n.listener.DeleteKeyListener;
|
||||||
import de.marhali.easyi18n.listener.PopupClickListener;
|
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.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.tabs.mapper.TreeModelMapper;
|
||||||
import de.marhali.easyi18n.util.TreeUtil;
|
import de.marhali.easyi18n.util.TreeUtil;
|
||||||
|
|
||||||
@ -28,6 +32,8 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.tree.DefaultMutableTreeNode;
|
import javax.swing.tree.DefaultMutableTreeNode;
|
||||||
import javax.swing.tree.TreePath;
|
import javax.swing.tree.TreePath;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -80,7 +86,21 @@ public class TreeView implements BusListener {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onUpdateData(@NotNull TranslationData data) {
|
public void onUpdateData(@NotNull TranslationData data) {
|
||||||
tree.setModel(this.currentMapper = new TreeModelMapper(data, SettingsService.getInstance(project).getState()));
|
List<Integer> expanded = getExpandedRows();
|
||||||
|
tree.setModel(this.currentMapper = new TreeModelMapper(data, ProjectSettingsService.get(project).getState()));
|
||||||
|
expanded.forEach(tree::expandRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Integer> getExpandedRows() {
|
||||||
|
List<Integer> expanded = new ArrayList<>();
|
||||||
|
|
||||||
|
for(int i = 0; i < tree.getRowCount(); i++) {
|
||||||
|
if(tree.isExpanded(i)) {
|
||||||
|
expanded.add(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return expanded;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -127,13 +147,13 @@ public class TreeView implements BusListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
KeyPath fullPath = TreeUtil.getFullPath(path);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
new EditDialog(project, new KeyedTranslation(fullPath, translation)).showAndHandle();
|
new EditDialog(project, new Translation(fullPath, value)).showAndHandle();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void deleteSelectedNodes() {
|
private void deleteSelectedNodes() {
|
||||||
@ -147,7 +167,7 @@ public class TreeView implements BusListener {
|
|||||||
KeyPath fullPath = TreeUtil.getFullPath(path);
|
KeyPath fullPath = TreeUtil.getFullPath(path);
|
||||||
|
|
||||||
InstanceManager.get(project).processUpdate(
|
InstanceManager.get(project).processUpdate(
|
||||||
new TranslationDelete(new KeyedTranslation(fullPath, null))
|
new TranslationDelete(new Translation(fullPath, null))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,13 @@
|
|||||||
package de.marhali.easyi18n.tabs.mapper;
|
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.FilterMissingTranslationsListener;
|
||||||
import de.marhali.easyi18n.model.bus.SearchQueryListener;
|
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.Nls;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@ -50,10 +55,10 @@ public class TableModelMapper implements TableModel, SearchQueryListener, Filter
|
|||||||
List<KeyPath> matches = new ArrayList<>();
|
List<KeyPath> matches = new ArrayList<>();
|
||||||
|
|
||||||
for(KeyPath key : this.data.getFullKeys()) {
|
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);
|
matches.add(key);
|
||||||
} else {
|
} else {
|
||||||
for(String content : this.data.getTranslation(key).values()) {
|
for(String content : this.data.getTranslation(key).getLocaleContents()) {
|
||||||
if(content.toLowerCase().contains(query)) {
|
if(content.toLowerCase().contains(query)) {
|
||||||
matches.add(key);
|
matches.add(key);
|
||||||
}
|
}
|
||||||
@ -74,7 +79,7 @@ public class TableModelMapper implements TableModel, SearchQueryListener, Filter
|
|||||||
List<KeyPath> matches = new ArrayList<>();
|
List<KeyPath> matches = new ArrayList<>();
|
||||||
|
|
||||||
for(KeyPath key : this.data.getFullKeys()) {
|
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);
|
matches.add(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -117,25 +122,25 @@ public class TableModelMapper implements TableModel, SearchQueryListener, Filter
|
|||||||
KeyPath key = this.fullKeys.get(rowIndex);
|
KeyPath key = this.fullKeys.get(rowIndex);
|
||||||
|
|
||||||
if(columnIndex == 0) { // Keys
|
if(columnIndex == 0) { // Keys
|
||||||
return this.converter.concat(key);
|
return this.converter.toString(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
String locale = this.locales.get(columnIndex - 1);
|
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
|
@Override
|
||||||
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
|
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
|
||||||
KeyPath key = this.fullKeys.get(rowIndex);
|
KeyPath key = this.fullKeys.get(rowIndex);
|
||||||
Translation translation = this.data.getTranslation(key);
|
TranslationValue translation = this.data.getTranslation(key);
|
||||||
|
|
||||||
if(translation == null) { // Unknown cell
|
if(translation == null) { // Unknown cell
|
||||||
return;
|
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
|
// Translation content update
|
||||||
if(columnIndex > 0) {
|
if(columnIndex > 0) {
|
||||||
@ -146,8 +151,8 @@ public class TableModelMapper implements TableModel, SearchQueryListener, Filter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TranslationUpdate update = new TranslationUpdate(new KeyedTranslation(key, translation),
|
TranslationUpdate update = new TranslationUpdate(new Translation(key, translation),
|
||||||
new KeyedTranslation(newKey, translation));
|
new Translation(newKey, translation));
|
||||||
|
|
||||||
this.updater.accept(update);
|
this.updater.accept(update);
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,14 @@ package de.marhali.easyi18n.tabs.mapper;
|
|||||||
import com.intellij.ide.projectView.PresentationData;
|
import com.intellij.ide.projectView.PresentationData;
|
||||||
import com.intellij.ui.JBColor;
|
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.FilterMissingTranslationsListener;
|
||||||
import de.marhali.easyi18n.model.bus.SearchQueryListener;
|
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 de.marhali.easyi18n.util.UiUtil;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@ -24,13 +29,13 @@ public class TreeModelMapper extends DefaultTreeModel implements SearchQueryList
|
|||||||
|
|
||||||
private final TranslationData data;
|
private final TranslationData data;
|
||||||
private final KeyPathConverter converter;
|
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);
|
super(null);
|
||||||
|
|
||||||
this.data = data;
|
this.data = data;
|
||||||
this.converter = new KeyPathConverter(state.isNestedKeys());
|
this.converter = new KeyPathConverter(state);
|
||||||
this.state = state;
|
this.state = state;
|
||||||
|
|
||||||
DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode();
|
DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode();
|
||||||
@ -41,7 +46,7 @@ public class TreeModelMapper extends DefaultTreeModel implements SearchQueryList
|
|||||||
@Override
|
@Override
|
||||||
public void onSearchQuery(@Nullable String query) {
|
public void onSearchQuery(@Nullable String query) {
|
||||||
DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode();
|
DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode();
|
||||||
TranslationData shadow = new TranslationData(this.state.isSortKeys());
|
TranslationData shadow = new TranslationData(this.state.isSorting());
|
||||||
|
|
||||||
if(query == null) { // Reset
|
if(query == null) { // Reset
|
||||||
this.generateNodes(rootNode, this.data.getRootNode());
|
this.generateNodes(rootNode, this.data.getRootNode());
|
||||||
@ -52,15 +57,15 @@ public class TreeModelMapper extends DefaultTreeModel implements SearchQueryList
|
|||||||
query = query.toLowerCase();
|
query = query.toLowerCase();
|
||||||
|
|
||||||
for(KeyPath currentKey : this.data.getFullKeys()) {
|
for(KeyPath currentKey : this.data.getFullKeys()) {
|
||||||
Translation translation = this.data.getTranslation(currentKey);
|
TranslationValue translation = this.data.getTranslation(currentKey);
|
||||||
String loweredKey = this.converter.concat(currentKey).toLowerCase();
|
String loweredKey = this.converter.toString(currentKey).toLowerCase();
|
||||||
|
|
||||||
if(query.contains(loweredKey) || loweredKey.contains(query)) {
|
if(query.contains(loweredKey) || loweredKey.contains(query)) {
|
||||||
shadow.setTranslation(currentKey, translation);
|
shadow.setTranslation(currentKey, translation);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for(String currentContent : translation.values()) {
|
for(String currentContent : translation.getLocaleContents()) {
|
||||||
if(currentContent.toLowerCase().contains(query)) {
|
if(currentContent.toLowerCase().contains(query)) {
|
||||||
shadow.setTranslation(currentKey, translation);
|
shadow.setTranslation(currentKey, translation);
|
||||||
break;
|
break;
|
||||||
@ -75,7 +80,7 @@ public class TreeModelMapper extends DefaultTreeModel implements SearchQueryList
|
|||||||
@Override
|
@Override
|
||||||
public void onFilterMissingTranslations(boolean filter) {
|
public void onFilterMissingTranslations(boolean filter) {
|
||||||
DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode();
|
DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode();
|
||||||
TranslationData shadow = new TranslationData(this.state.isSortKeys());
|
TranslationData shadow = new TranslationData(this.state.isSorting());
|
||||||
|
|
||||||
if(!filter) { // Reset
|
if(!filter) { // Reset
|
||||||
this.generateNodes(rootNode, this.data.getRootNode());
|
this.generateNodes(rootNode, this.data.getRootNode());
|
||||||
@ -84,9 +89,9 @@ public class TreeModelMapper extends DefaultTreeModel implements SearchQueryList
|
|||||||
}
|
}
|
||||||
|
|
||||||
for(KeyPath currentKey : this.data.getFullKeys()) {
|
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);
|
shadow.setTranslation(currentKey, translation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -124,7 +129,7 @@ public class TreeModelMapper extends DefaultTreeModel implements SearchQueryList
|
|||||||
} else {
|
} else {
|
||||||
String previewLocale = this.state.getPreviewLocale();
|
String previewLocale = this.state.getPreviewLocale();
|
||||||
String sub = "(" + previewLocale + ": " + childTranslationNode.getValue().get(previewLocale) + ")";
|
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);
|
PresentationData data = new PresentationData(key, sub, null, null);
|
||||||
data.setTooltip(tooltip);
|
data.setTooltip(tooltip);
|
||||||
|
159
src/main/java/de/marhali/easyi18n/util/KeyPathConverter.java
Normal file
159
src/main/java/de/marhali/easyi18n/util/KeyPathConverter.java
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
package de.marhali.easyi18n.util;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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(@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
|
||||||
|
* @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()) {
|
||||||
|
String namespaceDelim = (settings.isNestedKeys() ? "" : "\\") + settings.getNamespaceDelimiter();
|
||||||
|
if(section.length() == literalPath.length() || !literalPath.substring(section.length()).startsWith(namespaceDelim)) {
|
||||||
|
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()
|
||||||
|
? ("(?<!" + Pattern.quote("\\") + ")" + getSplitCharsRegex())
|
||||||
|
: Pattern.quote("\\") + getSplitCharsRegex();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getSplitCharsRegex() {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
|
||||||
|
builder.append("(");
|
||||||
|
builder.append(Pattern.quote(settings.getSectionDelimiter()));
|
||||||
|
|
||||||
|
// Add optional namespace delimiter if present
|
||||||
|
if(settings.getNamespaceDelimiter() != null && !settings.getNamespaceDelimiter().isEmpty()) {
|
||||||
|
builder.append("|");
|
||||||
|
builder.append(Pattern.quote(Objects.requireNonNull(settings.getNamespaceDelimiter())));
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.append(")");
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Securely escape found delimiters inside provided section according to the configured policy.
|
||||||
|
*/
|
||||||
|
private String quoteSection(String section) {
|
||||||
|
String quoted = section;
|
||||||
|
if(!settings.isNestedKeys()) {
|
||||||
|
return quoted;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(hasDefaultNamespace()) {
|
||||||
|
quoted = quoted.replace(settings.getNamespaceDelimiter(), "\\" + settings.getNamespaceDelimiter());
|
||||||
|
}
|
||||||
|
|
||||||
|
quoted = quoted.replace(settings.getSectionDelimiter(), "\\" + settings.getSectionDelimiter());
|
||||||
|
return quoted;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String unquoteSection(String section) {
|
||||||
|
String unquoted = section;
|
||||||
|
if(hasDefaultNamespace()) {
|
||||||
|
unquoted = unquoted.replace("\\" + settings.getNamespaceDelimiter(), settings.getNamespaceDelimiter());
|
||||||
|
}
|
||||||
|
|
||||||
|
unquoted = unquoted.replace("\\" + settings.getSectionDelimiter(), settings.getSectionDelimiter());
|
||||||
|
return unquoted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Securely escape provided delimiter according to the configured policy.
|
||||||
|
*/
|
||||||
|
private String quoteDelimiter(String delimiter) {
|
||||||
|
return settings.isNestedKeys() ? delimiter : delimiter.replace(delimiter, "\\" + delimiter);
|
||||||
|
}
|
||||||
|
}
|
@ -5,7 +5,7 @@ import com.intellij.openapi.diagnostic.Logger;
|
|||||||
import com.intellij.openapi.project.Project;
|
import com.intellij.openapi.project.Project;
|
||||||
import de.marhali.easyi18n.action.SettingsAction;
|
import de.marhali.easyi18n.action.SettingsAction;
|
||||||
import de.marhali.easyi18n.io.IOHandler;
|
import de.marhali.easyi18n.io.IOHandler;
|
||||||
import de.marhali.easyi18n.model.SettingsState;
|
import de.marhali.easyi18n.settings.ProjectSettings;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
@ -17,11 +17,11 @@ import java.util.ResourceBundle;
|
|||||||
*/
|
*/
|
||||||
public class NotificationHelper {
|
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");
|
ResourceBundle bundle = ResourceBundle.getBundle("messages");
|
||||||
|
|
||||||
String message = MessageFormat.format(bundle.getString("error.io"),
|
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);
|
Logger.getInstance(IOHandler.class).error(message, ex);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package de.marhali.easyi18n.util;
|
package de.marhali.easyi18n.util;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User interface utilities.
|
* User interface utilities.
|
||||||
@ -13,12 +14,12 @@ public class UiUtil {
|
|||||||
* @param messages Contains locales with desired translation
|
* @param messages Contains locales with desired translation
|
||||||
* @return String with html format
|
* @return String with html format
|
||||||
*/
|
*/
|
||||||
public static String generateHtmlTooltip(Map<String, String> messages) {
|
public static String generateHtmlTooltip(Set<Map.Entry<String, String>> messages) {
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
|
|
||||||
builder.append("<html>");
|
builder.append("<html>");
|
||||||
|
|
||||||
for(Map.Entry<String, String> entry : messages.entrySet()) {
|
for(Map.Entry<String, String> entry : messages) {
|
||||||
builder.append("<b>");
|
builder.append("<b>");
|
||||||
builder.append(entry.getKey()).append(":");
|
builder.append(entry.getKey()).append(":");
|
||||||
builder.append("</b> ");
|
builder.append("</b> ");
|
||||||
|
@ -1,5 +1,27 @@
|
|||||||
<idea-plugin>
|
<idea-plugin>
|
||||||
<extensions defaultExtensionNs="com.intellij">
|
<extensions defaultExtensionNs="com.intellij">
|
||||||
<lang.foldingBuilder language="JAVA" implementationClass="de.marhali.easyi18n.editor.generic.GenericFoldingBuilder" />
|
<intentionAction>
|
||||||
|
<className>de.marhali.easyi18n.assistance.intention.JavaTranslationIntention</className>
|
||||||
|
</intentionAction>
|
||||||
|
|
||||||
|
<psi.referenceContributor
|
||||||
|
language="JAVA"
|
||||||
|
implementation="de.marhali.easyi18n.assistance.reference.JavaKeyReferenceContributor"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<lang.foldingBuilder
|
||||||
|
language="JAVA"
|
||||||
|
implementationClass="de.marhali.easyi18n.assistance.folding.JavaFoldingBuilder"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<lang.documentationProvider
|
||||||
|
language="JAVA"
|
||||||
|
implementationClass="de.marhali.easyi18n.assistance.documentation.CommonDocumentationProvider"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<completion.contributor
|
||||||
|
language="JAVA"
|
||||||
|
implementationClass="de.marhali.easyi18n.assistance.completion.JavaCompletionContributor"
|
||||||
|
/>
|
||||||
</extensions>
|
</extensions>
|
||||||
</idea-plugin>
|
</idea-plugin>
|
@ -1,6 +1,45 @@
|
|||||||
<idea-plugin>
|
<idea-plugin>
|
||||||
<extensions defaultExtensionNs="com.intellij">
|
<extensions defaultExtensionNs="com.intellij">
|
||||||
<lang.foldingBuilder language="JavaScript" implementationClass="de.marhali.easyi18n.editor.generic.GenericFoldingBuilder" />
|
<psi.referenceContributor
|
||||||
<lang.foldingBuilder language="TypeScript" implementationClass="de.marhali.easyi18n.editor.generic.GenericFoldingBuilder" />
|
language="JavaScript"
|
||||||
|
implementation="de.marhali.easyi18n.assistance.reference.JsKeyReferenceContributor"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<lang.documentationProvider
|
||||||
|
language="JavaScript"
|
||||||
|
implementationClass="de.marhali.easyi18n.assistance.documentation.CommonDocumentationProvider"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- JavaScript plugin also includes TypeScript -->
|
||||||
|
<lang.documentationProvider
|
||||||
|
language="TypeScript"
|
||||||
|
implementationClass="de.marhali.easyi18n.assistance.documentation.CommonDocumentationProvider"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- JavaScript plugin also includes TypeScript -->
|
||||||
|
<lang.foldingBuilder
|
||||||
|
language="TypeScript"
|
||||||
|
implementationClass="de.marhali.easyi18n.assistance.folding.JsFoldingBuilder"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- JavaScript plugin also includes TypeScript JSX -->
|
||||||
|
<lang.foldingBuilder
|
||||||
|
language="TypeScript JSX"
|
||||||
|
implementationClass="de.marhali.easyi18n.assistance.folding.JsFoldingBuilder"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<lang.foldingBuilder
|
||||||
|
language="JavaScript"
|
||||||
|
implementationClass="de.marhali.easyi18n.assistance.folding.JsFoldingBuilder"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<intentionAction>
|
||||||
|
<className>de.marhali.easyi18n.assistance.intention.JsTranslationIntention</className>
|
||||||
|
</intentionAction>
|
||||||
|
|
||||||
|
<completion.contributor
|
||||||
|
language="JavaScript"
|
||||||
|
implementationClass="de.marhali.easyi18n.assistance.completion.JsCompletionContributor"
|
||||||
|
/>
|
||||||
</extensions>
|
</extensions>
|
||||||
</idea-plugin>
|
</idea-plugin>
|
@ -1,11 +1,27 @@
|
|||||||
<idea-plugin>
|
<idea-plugin>
|
||||||
<extensions defaultExtensionNs="com.intellij">
|
<extensions defaultExtensionNs="com.intellij">
|
||||||
<annotator language="kotlin" implementationClass="de.marhali.easyi18n.editor.kotlin.KotlinKeyAnnotator" />
|
<intentionAction>
|
||||||
|
<className>de.marhali.easyi18n.assistance.intention.KtTranslationIntention</className>
|
||||||
|
</intentionAction>
|
||||||
|
|
||||||
<completion.contributor language="kotlin"
|
<psi.referenceContributor
|
||||||
implementationClass="de.marhali.easyi18n.editor.kotlin.KotlinKeyCompletionContributor" />
|
language="kotlin"
|
||||||
|
implementation="de.marhali.easyi18n.assistance.reference.KtKeyReferenceContributor"
|
||||||
|
/>
|
||||||
|
|
||||||
<psi.referenceContributor language="kotlin"
|
<lang.documentationProvider
|
||||||
implementation="de.marhali.easyi18n.editor.kotlin.KotlinKeyReferenceContributor" />
|
language="kotlin"
|
||||||
|
implementationClass="de.marhali.easyi18n.assistance.documentation.CommonDocumentationProvider"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<completion.contributor
|
||||||
|
language="kotlin"
|
||||||
|
implementationClass="de.marhali.easyi18n.assistance.completion.KtCompletionContributor"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<lang.foldingBuilder
|
||||||
|
language="kotlin"
|
||||||
|
implementationClass="de.marhali.easyi18n.assistance.folding.KtFoldingBuilder"
|
||||||
|
/>
|
||||||
</extensions>
|
</extensions>
|
||||||
</idea-plugin>
|
</idea-plugin>
|
27
src/main/resources/META-INF/de.marhali.easyi18n-php.xml
Normal file
27
src/main/resources/META-INF/de.marhali.easyi18n-php.xml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<idea-plugin>
|
||||||
|
<extensions defaultExtensionNs="com.intellij">
|
||||||
|
<intentionAction>
|
||||||
|
<className>de.marhali.easyi18n.assistance.intention.PhpTranslationIntention</className>
|
||||||
|
</intentionAction>
|
||||||
|
|
||||||
|
<psi.referenceContributor
|
||||||
|
language="PHP"
|
||||||
|
implementation="de.marhali.easyi18n.assistance.reference.PhpKeyReferenceContributor"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<lang.foldingBuilder
|
||||||
|
language="PHP"
|
||||||
|
implementationClass="de.marhali.easyi18n.assistance.folding.PhpFoldingBuilder"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<lang.documentationProvider
|
||||||
|
language="PHP"
|
||||||
|
implementationClass="de.marhali.easyi18n.assistance.documentation.CommonDocumentationProvider"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<completion.contributor
|
||||||
|
language="PHP"
|
||||||
|
implementationClass="de.marhali.easyi18n.assistance.completion.PhpCompletionContributor"
|
||||||
|
/>
|
||||||
|
</extensions>
|
||||||
|
</idea-plugin>
|
@ -1,6 +1,15 @@
|
|||||||
<idea-plugin>
|
<idea-plugin>
|
||||||
<extensions defaultExtensionNs="com.intellij">
|
<extensions defaultExtensionNs="com.intellij">
|
||||||
<lang.foldingBuilder language="HTML"
|
<lang.foldingBuilder language="VueJS" implementationClass="de.marhali.easyi18n.assistance.folding.JsFoldingBuilder" />
|
||||||
implementationClass="de.marhali.easyi18n.editor.generic.GenericFoldingBuilder" />
|
|
||||||
|
<lang.documentationProvider
|
||||||
|
language="VueJS"
|
||||||
|
implementationClass="de.marhali.easyi18n.assistance.documentation.CommonDocumentationProvider"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<lang.documentationProvider
|
||||||
|
language="Vue"
|
||||||
|
implementationClass="de.marhali.easyi18n.assistance.documentation.CommonDocumentationProvider"
|
||||||
|
/>
|
||||||
</extensions>
|
</extensions>
|
||||||
</idea-plugin>
|
</idea-plugin>
|
@ -13,6 +13,7 @@
|
|||||||
<depends optional="true" config-file="de.marhali.easyi18n-javascript.xml">JavaScript</depends>
|
<depends optional="true" config-file="de.marhali.easyi18n-javascript.xml">JavaScript</depends>
|
||||||
<depends optional="true" config-file="de.marhali.easyi18n-java.xml">com.intellij.java</depends>
|
<depends optional="true" config-file="de.marhali.easyi18n-java.xml">com.intellij.java</depends>
|
||||||
<depends optional="true" config-file="de.marhali.easyi18n-vue.xml">org.jetbrains.plugins.vue</depends>
|
<depends optional="true" config-file="de.marhali.easyi18n-vue.xml">org.jetbrains.plugins.vue</depends>
|
||||||
|
<depends optional="true" config-file="de.marhali.easyi18n-php.xml">com.jetbrains.php</depends>
|
||||||
|
|
||||||
<actions>
|
<actions>
|
||||||
<action
|
<action
|
||||||
@ -24,21 +25,18 @@
|
|||||||
</actions>
|
</actions>
|
||||||
|
|
||||||
<extensions defaultExtensionNs="com.intellij">
|
<extensions defaultExtensionNs="com.intellij">
|
||||||
<toolWindow id="Easy I18n" anchor="bottom" factoryClass="de.marhali.easyi18n.service.TranslatorToolWindowFactory" />
|
<toolWindow id="Easy I18n" anchor="bottom"
|
||||||
|
factoryClass="de.marhali.easyi18n.service.TranslatorToolWindowFactory"
|
||||||
|
icon="/icons/translate13.svg"/>
|
||||||
|
|
||||||
<projectService serviceImplementation="de.marhali.easyi18n.service.SettingsService" />
|
<projectService serviceImplementation="de.marhali.easyi18n.settings.ProjectSettingsService"/>
|
||||||
|
|
||||||
<completion.contributor language="any"
|
<projectConfigurable parentId="tools" instance="de.marhali.easyi18n.settings.ProjectSettingsConfigurable"
|
||||||
implementationClass="de.marhali.easyi18n.editor.generic.GenericKeyCompletionContributor" />
|
id="de.marhali.easyi18n.service.AppSettingsConfigurable"
|
||||||
|
displayName="Easy I18n" nonDefaultProject="true"/>
|
||||||
|
|
||||||
<annotator language=""
|
<notificationGroup displayType="BALLOON" id="Easy I18n Notification Group"/>
|
||||||
implementationClass="de.marhali.easyi18n.editor.generic.GenericKeyAnnotator" />
|
|
||||||
|
|
||||||
<psi.referenceContributor
|
<errorHandler implementation="de.marhali.easyi18n.service.ErrorReportHandler"/>
|
||||||
implementation="de.marhali.easyi18n.editor.generic.GenericKeyReferenceContributor" />
|
|
||||||
|
|
||||||
<notificationGroup displayType="BALLOON" id="Easy I18n Notification Group" />
|
|
||||||
|
|
||||||
<errorHandler implementation="de.marhali.easyi18n.service.ErrorReportHandler" />
|
|
||||||
</extensions>
|
</extensions>
|
||||||
</idea-plugin>
|
</idea-plugin>
|
@ -1,9 +0,0 @@
|
|||||||
<svg width="13" height="13" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<!-- Created with Method Draw - http://github.com/duopixel/Method-Draw/ -->
|
|
||||||
<g id="Layer_1">
|
|
||||||
<title>Layer 1</title>
|
|
||||||
<line stroke-linecap="null" stroke-linejoin="null" id="svg_16" y2="10.12481" x2="3.98555" y1="3.34971" x1="3.98555" opacity="undefined" fill-opacity="null" stroke-opacity="null" stroke-dasharray="null" stroke-width="null" stroke="#000000" fill="none"/>
|
|
||||||
<line stroke-linecap="null" stroke-linejoin="null" id="svg_17" y2="3.69653" x2="1.32659" y1="3.69653" x1="6.76012" opacity="undefined" fill-opacity="null" stroke-opacity="null" stroke-dasharray="null" stroke-width="null" stroke="#000000" fill="none"/>
|
|
||||||
<line stroke-linecap="null" stroke-linejoin="null" id="svg_21" y2="9.53468" x2="10.97977" y1="9.53468" x1="6.4711" opacity="undefined" fill-opacity="null" stroke-opacity="null" stroke-dasharray="null" stroke-width="null" stroke="#000000" fill="none"/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 959 B |
4
src/main/resources/icons/translate13.svg
Normal file
4
src/main/resources/icons/translate13.svg
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 13 13">
|
||||||
|
<path stroke="#6E6E6E" stroke-width="2" stroke-linecap="undefined" stroke-linejoin="undefined" fill="none"
|
||||||
|
d="M4.457 2.992v8.995M8.064 2.027H1.006M7.939 10.987h4.047"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 273 B |
4
src/main/resources/icons/translate13_dark.svg
Normal file
4
src/main/resources/icons/translate13_dark.svg
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 13 13">
|
||||||
|
<path stroke="#AFB1B3" stroke-width="2" stroke-linecap="undefined" stroke-linejoin="undefined" fill="none"
|
||||||
|
d="M4.457 2.992v8.995M8.064 2.027H1.006M7.939 10.987h4.047"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 273 B |
@ -1,7 +1,8 @@
|
|||||||
view.tree.title=TreeView
|
documentation=EasyI18n Translation
|
||||||
|
view.tree.title=Tree View
|
||||||
view.tree.collapse=Collapse Tree
|
view.tree.collapse=Collapse Tree
|
||||||
view.tree.expand=Expand 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
|
view.empty=No translations found. Click on settings to specify a locales directory or reload
|
||||||
action.add=Add Translation
|
action.add=Add Translation
|
||||||
action.edit=Edit Translation
|
action.edit=Edit Translation
|
||||||
@ -10,21 +11,51 @@ action.reload=Reload From Disk
|
|||||||
action.settings=Settings
|
action.settings=Settings
|
||||||
action.search=Search...
|
action.search=Search...
|
||||||
action.delete=Delete
|
action.delete=Delete
|
||||||
|
action.extract=Extract translation
|
||||||
translation.key=Key
|
translation.key=Key
|
||||||
translation.locales=Locales
|
translation.locales=Locales
|
||||||
settings.path.title=Locales Directory
|
# Settings
|
||||||
settings.path.text=Locales directory
|
settings.hint.text=Project-specific configuration. For an easy start, you can use one of the existing presets.
|
||||||
settings.strategy.title=Translation file structure
|
settings.hint.action=Fore more information, see the documentation
|
||||||
settings.strategy.folder=Single Directory;Modularized: Locale / Namespace;Modularized: Namespace / Locale
|
settings.preset.title=Preset
|
||||||
settings.strategy.folder.tooltip=What is the folder structure of your translation files?
|
settings.preset.tooltip=Choose a configuration template that best fits your project. After that you can make further changes.
|
||||||
settings.strategy.parser=JSON;JSON5;YAML;YML;Properties;ARB
|
# Resource Configuration
|
||||||
settings.strategy.parser.tooltip=Which file parser should be used to process your translation files?
|
settings.resource.title=Resource Configuration
|
||||||
settings.strategy.file-pattern.tooltip=Defines a wildcard matcher to filter relevant translation files. For example *.json, *.???.
|
settings.resource.path.window=Locales Directory
|
||||||
settings.preview=Preview locale
|
settings.resource.path.title=Locales directory
|
||||||
settings.path.prefix=Path prefix
|
settings.resource.path.tooltip=Define the folder which contains all translation files. For nested folders, use the top folder.
|
||||||
settings.keys.sort=Sort translation keys alphabetically
|
settings.resource.strategy=File structure
|
||||||
settings.keys.nested=Escape delimiter character within a section layer.
|
settings.resource.folder.items=Single Directory;Modularized: Locale / Namespace;Modularized: Namespace / Locale
|
||||||
settings.editor.assistance=I18n key completion, annotation and reference inside editor
|
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.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]
|
||||||
|
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.
|
||||||
|
# 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\
|
error.io=An error occurred while processing translation files. \n\
|
||||||
Config: {0} => {1} ({2}) \n\
|
Config: {0} => {1} ({2}) \n\
|
||||||
Path: {3} \n\
|
Path: {3} \n\
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user