feat(i18n): allow selecting message XML file via file browser
- Updated the I18n settings to allow selecting the message bundle XML file directly via a file browser (TextFieldWithBrowseButton). - Modified I18nUtils to load message files via absolute paths from project settings, improving reliability. - Bumped plugin version to 3.2.2 and updated change notes.
This commit is contained in:
@@ -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 = """
|
||||
<h2>[3.2.2]</h2>
|
||||
<ul>
|
||||
<li><strong>UI/UX Improvement:</strong> Updated the I18n settings to allow selecting the message bundle XML file directly via a file browser, improving configuration usability.</li>
|
||||
<li><strong>Core Enhancement:</strong> Modified I18nUtils to load message files via absolute paths from project settings, increasing reliability across different project structures.</li>
|
||||
</ul>
|
||||
<h2>[3.2.1]</h2>
|
||||
<ul>
|
||||
<li><strong>Bug Fix:</strong> Resolved a <code>ConcurrentModificationException</code> in the I18n cache mechanism caused by concurrent background thread access.</li>
|
||||
</ul>
|
||||
<h2>[3.2.0]</h2>
|
||||
<ul>
|
||||
<li><strong>Schema Validation:</strong> Introduced project-level automatic XSD registration. Configure a target folder and namespace prefix to seamlessly map <code>.xsd</code> files to the <code>ExternalResourceManager</code>.</li>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ public class DynFormSettings implements PersistentStateComponent<DynFormSettings
|
||||
|
||||
public DisplayMode displayMode = DisplayMode.FOLDING;
|
||||
public boolean showIcon = true;
|
||||
public String i18nMessageFile = "message.xml";
|
||||
public String xsdFolderPath = "";
|
||||
public String xsdPrefix = "/dynf";
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.sdk.dynform.tools.i18n;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
@@ -9,31 +10,50 @@ import com.intellij.psi.search.FilenameIndex;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.xml.XmlFile;
|
||||
import com.intellij.psi.xml.XmlTag;
|
||||
import com.sdk.dynform.tools.config.DynFormSettings;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class I18nUtils {
|
||||
|
||||
private static final List<String> MESSAGE_FILENAMES = Arrays.asList("message.xml", "message_th.xml", "message_en.xml");
|
||||
|
||||
@NotNull
|
||||
public static List<XmlFile> findMessageFiles(@NotNull Project project) {
|
||||
DynFormSettings settings = DynFormSettings.getInstance(project);
|
||||
String filePath = settings.i18nMessageFile.trim();
|
||||
|
||||
List<XmlFile> result = new ArrayList<>();
|
||||
for (String filename : MESSAGE_FILENAMES) {
|
||||
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<VirtualFile> files = FilenameIndex.getVirtualFilesByName(filename, GlobalSearchScope.everythingScope(project));
|
||||
for (VirtualFile file : files) {
|
||||
PsiFile psiFile = PsiManager.getInstance(project).findFile(file);
|
||||
for (VirtualFile vFile : files) {
|
||||
addXmlFile(project, vFile, result);
|
||||
}
|
||||
} else {
|
||||
addXmlFile(project, file, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void addXmlFile(Project project, VirtualFile virtualFile, List<XmlFile> result) {
|
||||
PsiFile psiFile = PsiManager.getInstance(project).findFile(virtualFile);
|
||||
if (psiFile instanceof XmlFile) {
|
||||
result.add((XmlFile) psiFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static final Map<String, Map<String, String>> cache = new LinkedHashMap<>();
|
||||
private static final Map<String, Map<String, String>> 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;
|
||||
|
||||
@@ -34,6 +34,15 @@
|
||||
]]></description>
|
||||
|
||||
<change-notes><![CDATA[
|
||||
<h2>[3.2.2]</h2>
|
||||
<ul>
|
||||
<li><strong>UI/UX Improvement:</strong> Updated the I18n settings to allow selecting the message bundle XML file directly via a file browser, improving configuration usability.</li>
|
||||
<li><strong>Core Enhancement:</strong> Modified I18nUtils to load message files via absolute paths from project settings, increasing reliability across different project structures.</li>
|
||||
</ul>
|
||||
<h2>[3.2.1]</h2>
|
||||
<ul>
|
||||
<li><strong>Bug Fix:</strong> Resolved a <code>ConcurrentModificationException</code> in the I18n cache mechanism caused by concurrent background thread access.</li>
|
||||
</ul>
|
||||
<h2>[3.2.0]</h2>
|
||||
<ul>
|
||||
<li><strong>Schema Validation:</strong> Introduced project-level automatic XSD registration. Configure a target folder and namespace prefix to seamlessly map <code>.xsd</code> files to the <code>ExternalResourceManager</code>.</li>
|
||||
|
||||
Reference in New Issue
Block a user