Implement selectable text replacement with i18n keys

A new feature is introduced to replace the selected text in editor with Internationalization (i18n) keys, improving the coding workflow for developers handling string localization. The i18n keys are generated based on the customized settings or default flavor template. Further, a function to retrieve the key field from the `TranslationDialog` is added and a new utility class `DocumentUtil` for checking document file types has been added.
This commit is contained in:
JPilson 2023-12-16 17:51:29 +01:00
parent a709db810e
commit c56812c888
5 changed files with 94 additions and 5 deletions

View File

@ -4,10 +4,15 @@ import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project; import com.intellij.openapi.project.Project;
import de.marhali.easyi18n.dialog.AddDialog; import de.marhali.easyi18n.dialog.AddDialog;
import de.marhali.easyi18n.model.KeyPath; import de.marhali.easyi18n.model.KeyPath;
import de.marhali.easyi18n.settings.ProjectSettingsService;
import de.marhali.easyi18n.util.DocumentUtil;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
/** /**
@ -26,18 +31,40 @@ class LocalizeItAction extends AnAction {
public void actionPerformed(@NotNull AnActionEvent anActionEvent) { public void actionPerformed(@NotNull AnActionEvent anActionEvent) {
DataContext dataContext = anActionEvent.getDataContext(); DataContext dataContext = anActionEvent.getDataContext();
Editor editor = CommonDataKeys.EDITOR.getData(dataContext); Editor editor = CommonDataKeys.EDITOR.getData(dataContext);
if (editor == null) if (editor == null) return;
return;
String text = editor.getSelectionModel().getSelectedText(); String text = editor.getSelectionModel().getSelectedText();
if (text == null || text.isEmpty())
return;
if (text == null || text.isEmpty()) return;
if ((text.startsWith("\"") && text.endsWith("\"")) || (text.startsWith("'") && text.endsWith("'"))) {
text = text.substring(1);
text = text.substring(0, text.length() - 1);
}
Project project = anActionEvent.getProject(); Project project = anActionEvent.getProject();
if (project == null) { if (project == null) {
throw new RuntimeException("Project is null!"); throw new RuntimeException("Project is null!");
} }
AddDialog dialog = new AddDialog(project, new KeyPath(text), text); AddDialog dialog = new AddDialog(project, new KeyPath(text), text, (key) -> replaceSelectedText(project, editor, key));
dialog.showAndHandle(); dialog.showAndHandle();
}
private void replaceSelectedText(Project project, @NotNull Editor editor, @NotNull String key) {
int selectionStart = editor.getSelectionModel().getSelectionStart();
int selectionEnd = editor.getSelectionModel().getSelectionEnd();
String flavorTemplate = ProjectSettingsService.get(project).getState().getFlavorTemplate();
DocumentUtil documentUtil = new DocumentUtil(editor.getDocument());
String replacement = buildReplacement(flavorTemplate, key, documentUtil);
WriteCommandAction.runWriteCommandAction(editor.getProject(), () -> documentUtil.getDocument().replaceString(selectionStart, selectionEnd, replacement));
}
private String buildReplacement(String flavorTemplate, String key, DocumentUtil documentUtil) {
if (documentUtil.isVue() || documentUtil.isJsOrTs()) return flavorTemplate + "('" + key + "')";
return flavorTemplate + "(\"" + key + "\")";
} }
} }

View File

@ -13,6 +13,8 @@ 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 java.util.function.Consumer;
/** /**
* Dialog to create a new translation with all associated locale values. * Dialog to create a new translation with all associated locale values.
* Supports optional prefill technique for translation key or locale value. * Supports optional prefill technique for translation key or locale value.
@ -20,6 +22,8 @@ import org.jetbrains.annotations.Nullable;
*/ */
public class AddDialog extends TranslationDialog { public class AddDialog extends TranslationDialog {
private Consumer<String> onCreated;
/** /**
* Constructs a new create dialog with prefilled fields * Constructs a new create dialog with prefilled fields
* @param project Opened project * @param project Opened project
@ -35,6 +39,16 @@ public class AddDialog extends TranslationDialog {
setTitle(bundle.getString("action.add")); setTitle(bundle.getString("action.add"));
} }
public AddDialog(@NotNull Project project, @Nullable KeyPath prefillKey, @Nullable String prefillLocale,Consumer<String> onCreated) {
super(project, new Translation(prefillKey != null ? prefillKey : new KeyPath(),
prefillLocale != null
? new TranslationValue(ProjectSettingsService.get(project).getState().getPreviewLocale(), prefillLocale)
: null)
);
this.onCreated = onCreated;
setTitle(bundle.getString("action.add"));
}
/** /**
* Constructs a new create dialog without prefilled fields. * Constructs a new create dialog without prefilled fields.
@ -47,6 +61,7 @@ public class AddDialog extends TranslationDialog {
@Override @Override
protected @Nullable TranslationUpdate handleExit(int exitCode) { protected @Nullable TranslationUpdate handleExit(int exitCode) {
if(exitCode == DialogWrapper.OK_EXIT_CODE) { if(exitCode == DialogWrapper.OK_EXIT_CODE) {
if(onCreated != null) onCreated.accept(this.getKeyField().getText());
return new TranslationCreate(getState()); return new TranslationCreate(getState());
} }
return null; return null;

View File

@ -37,6 +37,10 @@ abstract class TranslationDialog extends DialogWrapper {
protected final @NotNull KeyPathConverter converter; protected final @NotNull KeyPathConverter converter;
protected final @NotNull Translation origin; protected final @NotNull Translation origin;
public JTextField getKeyField() {
return keyField;
}
protected final JTextField keyField; protected final JTextField keyField;
protected final Map<String, JTextField> localeValueFields; protected final Map<String, JTextField> localeValueFields;

View File

@ -86,4 +86,9 @@ public class DefaultPreset implements ProjectSettings {
public boolean isAlwaysFold() { public boolean isAlwaysFold() {
return false; return false;
} }
@Override
public String getFlavorTemplate() {
return "$i18n.t";
}
} }

View File

@ -0,0 +1,38 @@
package de.marhali.easyi18n.util;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.vfs.VirtualFile;
public class DocumentUtil {
protected Document document;
FileType fileType;
public Document getDocument() {
return document;
}
public void setDocument(Document document) {
this.document = document;
FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance();
VirtualFile virtualFile = fileDocumentManager.getFile(document);
if (virtualFile != null) {
fileType = virtualFile.getFileType();
}
}
public DocumentUtil(Document document) {
setDocument(document);
}
public boolean isJsOrTs() {
return (fileType.getDefaultExtension().contains("js") || fileType.getDescription().contains("ts"));
}
public boolean isVue() {
return fileType.getDefaultExtension().contains("vue");
}
}