diff --git a/build.gradle.kts b/build.gradle.kts
index 1e408a3..f218fe3 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -5,7 +5,7 @@ plugins {
}
group = "com.sdk.dynform.tools"
-version = "3.2.0"
+version = "3.2.2"
repositories {
mavenCentral()
@@ -39,6 +39,15 @@ intellijPlatform {
}
changeNotes = """
+
[3.2.2]
+
+ - UI/UX Improvement: Updated the I18n settings to allow selecting the message bundle XML file directly via a file browser, improving configuration usability.
+ - Core Enhancement: Modified I18nUtils to load message files via absolute paths from project settings, increasing reliability across different project structures.
+
+ [3.2.1]
+
+ - Bug Fix: Resolved a
ConcurrentModificationException in the I18n cache mechanism caused by concurrent background thread access.
+
[3.2.0]
- Schema Validation: Introduced project-level automatic XSD registration. Configure a target folder and namespace prefix to seamlessly map
.xsd files to the ExternalResourceManager.
diff --git a/src/main/java/com/sdk/dynform/tools/config/DynFormConfigurable.java b/src/main/java/com/sdk/dynform/tools/config/DynFormConfigurable.java
index aa95db9..b1d6f7f 100644
--- a/src/main/java/com/sdk/dynform/tools/config/DynFormConfigurable.java
+++ b/src/main/java/com/sdk/dynform/tools/config/DynFormConfigurable.java
@@ -1,11 +1,13 @@
package com.sdk.dynform.tools.config;
+import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.TextComponentAccessor;
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
import com.intellij.openapi.util.NlsContexts;
+import com.intellij.openapi.vfs.VirtualFile;
import com.sdk.dynform.tools.dynform.DynFormXsdScanner;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -20,6 +22,7 @@ public class DynFormConfigurable implements Configurable {
private JRadioButton inlayBtn;
private JRadioButton disabledBtn;
private JCheckBox showIconCheck;
+ private TextFieldWithBrowseButton i18nMessageFileField;
private TextFieldWithBrowseButton xsdFolderField;
private JTextField xsdPrefixField;
@@ -43,27 +46,58 @@ public class DynFormConfigurable implements Configurable {
gbc.insets = new Insets(5, 5, 5, 5);
// --- I18n Settings Group ---
- JPanel i18nPanel = new JPanel();
- i18nPanel.setLayout(new BoxLayout(i18nPanel, BoxLayout.Y_AXIS));
+ JPanel i18nPanel = new JPanel(new GridBagLayout());
i18nPanel.setBorder(BorderFactory.createTitledBorder("Internationalization (i18n)"));
- i18nPanel.add(new JLabel("Message Display Mode:"));
+ GridBagConstraints i18nGbc = new GridBagConstraints();
+ i18nGbc.fill = GridBagConstraints.HORIZONTAL;
+ i18nGbc.insets = new Insets(2, 2, 2, 2);
+ i18nGbc.gridx = 0;
+ i18nGbc.gridy = 0;
+ i18nGbc.weightx = 0.0;
+
+ i18nPanel.add(new JLabel("Message Display Mode:"), i18nGbc);
+
+ i18nGbc.gridy++;
foldingBtn = new JRadioButton("Folding (Hide key, show translation)");
+ i18nPanel.add(foldingBtn, i18nGbc);
+
+ i18nGbc.gridy++;
inlayBtn = new JRadioButton("Inlay Hints (Show translation next to key)");
+ i18nPanel.add(inlayBtn, i18nGbc);
+
+ i18nGbc.gridy++;
disabledBtn = new JRadioButton("Disabled");
+ i18nPanel.add(disabledBtn, i18nGbc);
ButtonGroup group = new ButtonGroup();
group.add(foldingBtn);
group.add(inlayBtn);
group.add(disabledBtn);
- i18nPanel.add(foldingBtn);
- i18nPanel.add(inlayBtn);
- i18nPanel.add(disabledBtn);
-
- i18nPanel.add(Box.createVerticalStrut(10));
+ i18nGbc.gridy++;
showIconCheck = new JCheckBox("Show icon in code completion");
- i18nPanel.add(showIconCheck);
+ i18nPanel.add(showIconCheck, i18nGbc);
+
+ i18nGbc.gridy++;
+ i18nGbc.weightx = 0.0;
+ i18nPanel.add(new JLabel("Message Bundle File:"), i18nGbc);
+
+ i18nGbc.gridx = 1;
+ i18nGbc.weightx = 1.0;
+ i18nMessageFileField = new TextFieldWithBrowseButton();
+
+ FileChooserDescriptor xmlDescriptor = new FileChooserDescriptor(true, false, false, false, false, false) {
+ @Override
+ public boolean isFileVisible(VirtualFile file, boolean showHiddenFiles) {
+ return super.isFileVisible(file, showHiddenFiles) && (file.isDirectory() || "xml".equalsIgnoreCase(file.getExtension()));
+ }
+ };
+
+ i18nMessageFileField.addBrowseFolderListener("Select Message XML File", "Select the main message bundle XML file",
+ project, xmlDescriptor, TextComponentAccessor.TEXT_FIELD_WHOLE_TEXT);
+
+ i18nPanel.add(i18nMessageFileField, i18nGbc);
mainPanel.add(i18nPanel, gbc);
@@ -121,6 +155,7 @@ public class DynFormConfigurable implements Configurable {
DynFormSettings settings = DynFormSettings.getInstance(project);
return settings.displayMode != getCurrentModeFromUI() ||
settings.showIcon != showIconCheck.isSelected() ||
+ !settings.i18nMessageFile.equals(i18nMessageFileField.getText()) ||
!settings.xsdFolderPath.equals(xsdFolderField.getText()) ||
!settings.xsdPrefix.equals(xsdPrefixField.getText());
}
@@ -130,6 +165,7 @@ public class DynFormConfigurable implements Configurable {
DynFormSettings settings = DynFormSettings.getInstance(project);
settings.displayMode = getCurrentModeFromUI();
settings.showIcon = showIconCheck.isSelected();
+ settings.i18nMessageFile = i18nMessageFileField.getText();
settings.xsdFolderPath = xsdFolderField.getText();
settings.xsdPrefix = xsdPrefixField.getText();
@@ -155,6 +191,7 @@ public class DynFormConfigurable implements Configurable {
case DISABLED: disabledBtn.setSelected(true); break;
}
showIconCheck.setSelected(settings.showIcon);
+ i18nMessageFileField.setText(settings.i18nMessageFile);
xsdFolderField.setText(settings.xsdFolderPath);
xsdPrefixField.setText(settings.xsdPrefix);
}
diff --git a/src/main/java/com/sdk/dynform/tools/config/DynFormSettings.java b/src/main/java/com/sdk/dynform/tools/config/DynFormSettings.java
index 3967f95..274dd74 100644
--- a/src/main/java/com/sdk/dynform/tools/config/DynFormSettings.java
+++ b/src/main/java/com/sdk/dynform/tools/config/DynFormSettings.java
@@ -22,6 +22,7 @@ public class DynFormSettings implements PersistentStateComponent MESSAGE_FILENAMES = Arrays.asList("message.xml", "message_th.xml", "message_en.xml");
-
@NotNull
public static List findMessageFiles(@NotNull Project project) {
+ DynFormSettings settings = DynFormSettings.getInstance(project);
+ String filePath = settings.i18nMessageFile.trim();
+
List result = new ArrayList<>();
- for (String filename : MESSAGE_FILENAMES) {
- Collection files = FilenameIndex.getVirtualFilesByName(filename, GlobalSearchScope.everythingScope(project));
- for (VirtualFile file : files) {
- PsiFile psiFile = PsiManager.getInstance(project).findFile(file);
- if (psiFile instanceof XmlFile) {
- result.add((XmlFile) psiFile);
- }
- }
+ if (filePath.isEmpty()) {
+ return result;
}
+
+ // Try to find the file using its absolute path
+ VirtualFile file = LocalFileSystem.getInstance().findFileByPath(filePath);
+
+ // If not found by absolute path, fallback to searching by filename in the project
+ if (file == null) {
+ String filename = new java.io.File(filePath).getName();
+ Collection files = FilenameIndex.getVirtualFilesByName(filename, GlobalSearchScope.everythingScope(project));
+ for (VirtualFile vFile : files) {
+ addXmlFile(project, vFile, result);
+ }
+ } else {
+ addXmlFile(project, file, result);
+ }
+
return result;
}
- private static final Map> cache = new LinkedHashMap<>();
+ private static void addXmlFile(Project project, VirtualFile virtualFile, List result) {
+ PsiFile psiFile = PsiManager.getInstance(project).findFile(virtualFile);
+ if (psiFile instanceof XmlFile) {
+ result.add((XmlFile) psiFile);
+ }
+ }
+
+ private static final Map> cache = new ConcurrentHashMap<>();
private static long lastCacheUpdate = 0;
private static final long CACHE_TIMEOUT = 5000; // 5 seconds
@@ -138,7 +158,7 @@ public class I18nUtils {
return result.toString().trim();
}
- private static void updateCache(@NotNull Project project) {
+ private static synchronized void updateCache(@NotNull Project project) {
long currentTime = System.currentTimeMillis();
if (currentTime - lastCacheUpdate < CACHE_TIMEOUT && !cache.isEmpty()) {
return;
diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml
index d2b4281..7e06c8c 100644
--- a/src/main/resources/META-INF/plugin.xml
+++ b/src/main/resources/META-INF/plugin.xml
@@ -34,6 +34,15 @@
]]>
[3.2.2]
+
+ - UI/UX Improvement: Updated the I18n settings to allow selecting the message bundle XML file directly via a file browser, improving configuration usability.
+ - Core Enhancement: Modified I18nUtils to load message files via absolute paths from project settings, increasing reliability across different project structures.
+
+ [3.2.1]
+
+ - Bug Fix: Resolved a
ConcurrentModificationException in the I18n cache mechanism caused by concurrent background thread access.
+
[3.2.0]
- Schema Validation: Introduced project-level automatic XSD registration. Configure a target folder and namespace prefix to seamlessly map
.xsd files to the ExternalResourceManager.