commit
da655d91ff
15
CHANGELOG.md
15
CHANGELOG.md
@ -3,12 +3,21 @@
|
||||
# easy-i18n Changelog
|
||||
|
||||
## [Unreleased]
|
||||
### Added
|
||||
- Support for IntelliJ Platform version 2022.3
|
||||
- Regex support for translation file patterns
|
||||
|
||||
### Changed
|
||||
- Reload function internally consolidated
|
||||
|
||||
### Fixed
|
||||
- Parsing for <kbd>.properties</kbd> files
|
||||
|
||||
## [4.2.4]
|
||||
### Changed
|
||||
- Improved exception handling on syntax errors
|
||||
### Changed
|
||||
- Improved exception handling on syntax errors
|
||||
|
||||
### Fixed
|
||||
### Fixed
|
||||
- Some settings are not retained on IDE restarts
|
||||
|
||||
## [4.2.3]
|
||||
|
@ -4,12 +4,12 @@
|
||||
pluginGroup = de.marhali.easyi18n
|
||||
pluginName = easy-i18n
|
||||
# SemVer format -> https://semver.org
|
||||
pluginVersion = 4.2.4
|
||||
pluginVersion = 4.3.0
|
||||
|
||||
# See https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
|
||||
# for insight into build numbers and IntelliJ Platform versions.
|
||||
pluginSinceBuild = 203
|
||||
pluginUntilBuild = 222.*
|
||||
pluginUntilBuild = 223
|
||||
|
||||
# IntelliJ Platform Properties -> https://github.com/JetBrains/gradle-intellij-plugin#intellij-platform-properties
|
||||
platformType = IU
|
||||
|
@ -49,9 +49,6 @@ public class DataStore {
|
||||
*/
|
||||
public void loadFromPersistenceLayer(@NotNull Consumer<Boolean> successResult) {
|
||||
ProjectSettings settings = ProjectSettingsService.get(project).getState();
|
||||
|
||||
ApplicationManager.getApplication().saveAll(); // Save opened files (required if new locales were added)
|
||||
|
||||
ApplicationManager.getApplication().runReadAction(() -> {
|
||||
try {
|
||||
this.data = new IOHandler(project, settings).read();
|
||||
|
@ -72,6 +72,7 @@ public class InstanceManager {
|
||||
* Fetches data from persistence layer and notifies all endpoints via {@link DataBus}.
|
||||
*/
|
||||
public void reload() {
|
||||
ApplicationManager.getApplication().saveAll(); // Save opened files (required if new locales were added)
|
||||
store.loadFromPersistenceLayer((success) ->
|
||||
bus.propagate().onUpdateData(store.getData()));
|
||||
}
|
||||
|
@ -7,8 +7,8 @@ import de.marhali.easyi18n.io.parser.ParserStrategyType;
|
||||
import de.marhali.easyi18n.model.TranslationData;
|
||||
import de.marhali.easyi18n.model.TranslationFile;
|
||||
import de.marhali.easyi18n.settings.ProjectSettings;
|
||||
import de.marhali.easyi18n.util.WildcardRegexMatcher;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
@ -51,7 +51,8 @@ public abstract class FolderStrategy {
|
||||
* @return true if file matches and should be processed
|
||||
*/
|
||||
protected boolean isFileRelevant(@NotNull VirtualFile file) {
|
||||
return !file.isDirectory() && FilenameUtils.wildcardMatch(file.getName(), this.settings.getFilePattern());
|
||||
return !file.isDirectory()
|
||||
&& WildcardRegexMatcher.matchWildcardRegex(file.getName(), settings.getFilePattern());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -26,7 +26,7 @@ import java.util.Objects;
|
||||
public class Json5ParserStrategy extends ParserStrategy {
|
||||
|
||||
private static final Json5 JSON5 = Json5.builder(builder ->
|
||||
builder.allowInvalidSurrogate().trailingComma().indentFactor(4).build());
|
||||
builder.allowInvalidSurrogate().trailingComma().indentFactor(2).build());
|
||||
|
||||
public Json5ParserStrategy(@NotNull ProjectSettings settings) {
|
||||
super(settings);
|
||||
|
@ -47,9 +47,7 @@ public class PropertiesParserStrategy extends ParserStrategy {
|
||||
throw new SyntaxException(ex.getMessage(), file);
|
||||
}
|
||||
|
||||
if(!input.isEmpty()) {
|
||||
PropertiesMapper.read(file.getLocale(), input, targetData, converter);
|
||||
}
|
||||
PropertiesMapper.read(file.getLocale(), input, targetData, converter);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,10 +54,7 @@ public class FileChangeListener implements AsyncFileListener {
|
||||
events.forEach((e) -> {
|
||||
if(e.getPath().contains(localesPath)) { // Perform reload
|
||||
logger.debug("Detected file change. Reloading instance...");
|
||||
InstanceManager manager = InstanceManager.get(project);
|
||||
manager.store().loadFromPersistenceLayer((success) -> {
|
||||
manager.bus().propagate().onUpdateData(manager.store().getData());
|
||||
});
|
||||
InstanceManager.get(project).reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ public class ProjectSettingsService implements PersistentStateComponent<ProjectS
|
||||
* Sets the provided configuration and invalidates the merged state.
|
||||
* @param state New configuration
|
||||
*/
|
||||
protected void setState(@NotNull ProjectSettingsState state) {
|
||||
public void setState(@NotNull ProjectSettingsState state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,23 @@
|
||||
package de.marhali.easyi18n.util;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
|
||||
/**
|
||||
* Utilities for wildcard / regex matching.
|
||||
* @author marhali
|
||||
*/
|
||||
public class WildcardRegexMatcher {
|
||||
public static boolean matchWildcardRegex(String string, String pattern) {
|
||||
boolean wildcardMatch = FilenameUtils.wildcardMatchOnSystem(string, pattern);
|
||||
|
||||
if(wildcardMatch) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
return string.matches(pattern);
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -46,7 +46,7 @@ public interface Section {
|
||||
}
|
||||
static String toString(Section section) {
|
||||
DumperOptions options = new DumperOptions();
|
||||
options.setIndent(4);
|
||||
options.setIndent(2);
|
||||
options.setAllowUnicode(true);
|
||||
options.setPrettyFlow(true);
|
||||
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
|
||||
|
@ -31,7 +31,7 @@ settings.resource.folder.items=Single Directory;Modularized: Locale / Namespace;
|
||||
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.file-pattern.tooltip=Defines a wildcard matcher or regex pattern to filter relevant translation files. For example *.json, *.??? or *.*.
|
||||
settings.resource.nesting.title=Consider subdirectories for modularized translation files
|
||||
settings.resource.nesting.tooltip=Validates directories within a module and passes them as a submodule.
|
||||
settings.resource.sorting.title=Sort translation keys alphabetically
|
||||
|
@ -0,0 +1,34 @@
|
||||
package de.marhali.easyi18n;
|
||||
|
||||
import de.marhali.easyi18n.util.WildcardRegexMatcher;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link WildcardRegexMatcher}.
|
||||
* @author marhali
|
||||
*/
|
||||
public class WildcardRegexMatcherTest extends WildcardRegexMatcher {
|
||||
@Test
|
||||
public void testWildcard() {
|
||||
Assert.assertTrue(matchWildcardRegex("en.json", "*.json"));
|
||||
Assert.assertTrue(matchWildcardRegex("de.json", "*.json"));
|
||||
Assert.assertFalse(matchWildcardRegex("index.html", "*.json"));
|
||||
|
||||
Assert.assertTrue(matchWildcardRegex("en.json", "*.*"));
|
||||
Assert.assertFalse(matchWildcardRegex("file", "*.*"));
|
||||
|
||||
Assert.assertTrue(matchWildcardRegex("en.txt", "*.???"));
|
||||
Assert.assertFalse(matchWildcardRegex("en.json", "*.???"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegex() {
|
||||
Assert.assertTrue(matchWildcardRegex("en.json", "^(en|de)\\.json"));
|
||||
Assert.assertFalse(matchWildcardRegex("gb.json", "^(en|de)\\.json"));
|
||||
|
||||
Assert.assertTrue(matchWildcardRegex("en.jpg", "^.*\\.(jpg|JPG|gif|GIF)$"));
|
||||
Assert.assertFalse(matchWildcardRegex("en.json", "^.*\\.(jpg|JPG|gif|GIF)$"));
|
||||
}
|
||||
}
|
85
src/test/java/de/marhali/easyi18n/e2e/EndToEndTestCase.java
Normal file
85
src/test/java/de/marhali/easyi18n/e2e/EndToEndTestCase.java
Normal file
@ -0,0 +1,85 @@
|
||||
package de.marhali.easyi18n.e2e;
|
||||
|
||||
import com.intellij.testFramework.fixtures.BasePlatformTestCase;
|
||||
|
||||
import de.marhali.easyi18n.InstanceManager;
|
||||
import de.marhali.easyi18n.settings.ProjectSettings;
|
||||
import de.marhali.easyi18n.settings.ProjectSettingsService;
|
||||
import de.marhali.easyi18n.settings.ProjectSettingsState;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.filefilter.IOFileFilter;
|
||||
import org.apache.commons.io.filefilter.TrueFileFilter;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* End-to-end test case.
|
||||
* @author marhali
|
||||
*/
|
||||
public abstract class EndToEndTestCase extends BasePlatformTestCase {
|
||||
|
||||
private static final Charset CHARSET = StandardCharsets.UTF_8;
|
||||
|
||||
private final ProjectSettings settings;
|
||||
private Path tempPath;
|
||||
|
||||
public EndToEndTestCase(ProjectSettings settings) {
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
ProjectSettingsService.get(getProject()).setState(new ProjectSettingsState(settings));
|
||||
tempPath = Files.createTempDirectory("tests-easyi18n-");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
FileUtils.deleteDirectory(tempPath.toFile());
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
public void testParseAndSerialize() throws IOException {
|
||||
// Read translation files based on the provided settings
|
||||
InstanceManager.get(getProject()).store().loadFromPersistenceLayer(success -> {});
|
||||
|
||||
// Save the cached translation data to a temporary output directory
|
||||
ProjectSettingsState out = new ProjectSettingsState(settings);
|
||||
out.setLocalesDirectory(tempPath.toString());
|
||||
ProjectSettingsService.get(getProject()).setState(out);
|
||||
|
||||
InstanceManager.get(getProject()).store().saveToPersistenceLayer(success -> {});
|
||||
|
||||
// Compare file structure and contents
|
||||
IOFileFilter fileFilter = TrueFileFilter.INSTANCE;
|
||||
|
||||
File originalDirectory = new File(Objects.requireNonNull(settings.getLocalesDirectory()));
|
||||
File[] originalFiles = FileUtils.listFiles(originalDirectory, fileFilter, fileFilter).toArray(new File[0]);
|
||||
|
||||
File outputDirectory = tempPath.toFile();
|
||||
File[] outputFiles = FileUtils.listFiles(outputDirectory, fileFilter, fileFilter).toArray(new File[0]);
|
||||
|
||||
Arrays.sort(originalFiles);
|
||||
Arrays.sort(outputFiles);
|
||||
|
||||
assertEquals(originalFiles.length, outputFiles.length);
|
||||
|
||||
for(int i = 0; i < originalFiles.length; i++) {
|
||||
File originalFile = originalFiles[i];
|
||||
File outputFile = outputFiles[i];
|
||||
|
||||
// Replace originalFile with os-dependent line-separators
|
||||
assertEquals(FileUtils.readFileToString(originalFile, CHARSET).replace("\n", System.lineSeparator()),
|
||||
FileUtils.readFileToString(outputFile, CHARSET));
|
||||
}
|
||||
}
|
||||
}
|
51
src/test/java/de/marhali/easyi18n/e2e/TestSettingsState.java
Normal file
51
src/test/java/de/marhali/easyi18n/e2e/TestSettingsState.java
Normal file
@ -0,0 +1,51 @@
|
||||
package de.marhali.easyi18n.e2e;
|
||||
|
||||
import de.marhali.easyi18n.io.folder.FolderStrategyType;
|
||||
import de.marhali.easyi18n.io.parser.ParserStrategyType;
|
||||
import de.marhali.easyi18n.settings.presets.DefaultPreset;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Settings base for end-to-end tests.
|
||||
* @author marhali
|
||||
*/
|
||||
public class TestSettingsState extends DefaultPreset {
|
||||
|
||||
private final String localesDirectory;
|
||||
private final FolderStrategyType folderStrategy;
|
||||
private final ParserStrategyType parserStrategy;
|
||||
|
||||
public TestSettingsState(String localesDirectory, FolderStrategyType folderStrategy, ParserStrategyType parserStrategy) {
|
||||
this.localesDirectory = localesDirectory;
|
||||
this.folderStrategy = folderStrategy;
|
||||
this.parserStrategy = parserStrategy;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public @Nullable String getLocalesDirectory() {
|
||||
return localesDirectory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull FolderStrategyType getFolderStrategy() {
|
||||
return folderStrategy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ParserStrategyType getParserStrategy() {
|
||||
return parserStrategy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getFilePattern() {
|
||||
return "*.*";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSorting() {
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package de.marhali.easyi18n.e2e.single;
|
||||
|
||||
import de.marhali.easyi18n.e2e.EndToEndTestCase;
|
||||
import de.marhali.easyi18n.e2e.TestSettingsState;
|
||||
import de.marhali.easyi18n.io.folder.FolderStrategyType;
|
||||
import de.marhali.easyi18n.io.parser.ParserStrategyType;
|
||||
|
||||
/**
|
||||
* @author marhali
|
||||
* End-to-end tests for single directory json5 files.
|
||||
*/
|
||||
public class SingleJson5Test extends EndToEndTestCase {
|
||||
public SingleJson5Test() {
|
||||
super(new TestSettingsState(
|
||||
"src/test/resources/single/json5",
|
||||
FolderStrategyType.SINGLE,
|
||||
ParserStrategyType.JSON5)
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package de.marhali.easyi18n.e2e.single;
|
||||
|
||||
import de.marhali.easyi18n.e2e.EndToEndTestCase;
|
||||
import de.marhali.easyi18n.e2e.TestSettingsState;
|
||||
import de.marhali.easyi18n.io.folder.FolderStrategyType;
|
||||
import de.marhali.easyi18n.io.parser.ParserStrategyType;
|
||||
|
||||
/**
|
||||
* End-to-end tests for single directory json files.
|
||||
* @author marhali
|
||||
*/
|
||||
public class SingleJsonTest extends EndToEndTestCase {
|
||||
public SingleJsonTest() {
|
||||
super(new TestSettingsState(
|
||||
"src/test/resources/single/json",
|
||||
FolderStrategyType.SINGLE,
|
||||
ParserStrategyType.JSON)
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package de.marhali.easyi18n.e2e.single;
|
||||
|
||||
import de.marhali.easyi18n.e2e.EndToEndTestCase;
|
||||
import de.marhali.easyi18n.e2e.TestSettingsState;
|
||||
import de.marhali.easyi18n.io.folder.FolderStrategyType;
|
||||
import de.marhali.easyi18n.io.parser.ParserStrategyType;
|
||||
|
||||
/**
|
||||
* End-to-end tests for single directory .properties files.
|
||||
* @author marhali
|
||||
*/
|
||||
public class SinglePropertiesTest extends EndToEndTestCase {
|
||||
public SinglePropertiesTest() {
|
||||
super(new TestSettingsState(
|
||||
"src/test/resources/single/properties",
|
||||
FolderStrategyType.SINGLE,
|
||||
ParserStrategyType.PROPERTIES)
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package de.marhali.easyi18n.e2e.single;
|
||||
|
||||
import de.marhali.easyi18n.e2e.EndToEndTestCase;
|
||||
import de.marhali.easyi18n.e2e.TestSettingsState;
|
||||
import de.marhali.easyi18n.io.folder.FolderStrategyType;
|
||||
import de.marhali.easyi18n.io.parser.ParserStrategyType;
|
||||
|
||||
/**
|
||||
* End-to-ends tests for single directory yaml files.
|
||||
* @author marhali
|
||||
*/
|
||||
public class SingleYamlTest extends EndToEndTestCase {
|
||||
public SingleYamlTest() {
|
||||
super(new TestSettingsState(
|
||||
"src/test/resources/single/yaml",
|
||||
FolderStrategyType.SINGLE,
|
||||
ParserStrategyType.YML)
|
||||
);
|
||||
}
|
||||
}
|
@ -10,9 +10,16 @@ import de.marhali.easyi18n.settings.presets.DefaultPreset;
|
||||
* @author marhali
|
||||
*/
|
||||
public class ProjectSettingsServiceTest extends BasePlatformTestCase {
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
ProjectSettingsService.get(getProject()).setState(new ProjectSettingsState());
|
||||
}
|
||||
|
||||
public void testSettingsDefaultPreset() {
|
||||
ProjectSettingsState state = ProjectSettingsService.get(getProject()).getState();
|
||||
assertEquals(state, new ProjectSettingsState(new DefaultPreset()));
|
||||
assertEquals(new ProjectSettingsState(new DefaultPreset()), state);
|
||||
}
|
||||
|
||||
public void testPersistenceState() {
|
||||
|
11
src/test/resources/single/json/de.json
Normal file
11
src/test/resources/single/json/de.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"title": "Titel",
|
||||
"number": 187,
|
||||
"object": {
|
||||
"title": "Titel"
|
||||
},
|
||||
"array": [
|
||||
"element1",
|
||||
"element2"
|
||||
]
|
||||
}
|
11
src/test/resources/single/json/en.json
Normal file
11
src/test/resources/single/json/en.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"title": "Title",
|
||||
"number": -187,
|
||||
"object": {
|
||||
"title": "Title"
|
||||
},
|
||||
"array": [
|
||||
"item1",
|
||||
"item2"
|
||||
]
|
||||
}
|
12
src/test/resources/single/json5/de.json5
Normal file
12
src/test/resources/single/json5/de.json5
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"title": "Titel",
|
||||
"number": 187,
|
||||
"hex": 0x187,
|
||||
"object": {
|
||||
"title": "Titel",
|
||||
},
|
||||
"array": [
|
||||
"element1",
|
||||
"element2",
|
||||
],
|
||||
}
|
12
src/test/resources/single/json5/en.json5
Normal file
12
src/test/resources/single/json5/en.json5
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"title": "Title",
|
||||
"number": -187,
|
||||
"hex": -0x187,
|
||||
"object": {
|
||||
"title": "Title",
|
||||
},
|
||||
"array": [
|
||||
"item1",
|
||||
"item2",
|
||||
],
|
||||
}
|
2
src/test/resources/single/properties/de.properties
Normal file
2
src/test/resources/single/properties/de.properties
Normal file
@ -0,0 +1,2 @@
|
||||
breakLine=eins\nzwei
|
||||
title=Titel
|
2
src/test/resources/single/properties/en.properties
Normal file
2
src/test/resources/single/properties/en.properties
Normal file
@ -0,0 +1,2 @@
|
||||
breakLine=first\nsecond
|
||||
title=Title
|
3
src/test/resources/single/yaml/de.yaml
Normal file
3
src/test/resources/single/yaml/de.yaml
Normal file
@ -0,0 +1,3 @@
|
||||
title: Titel
|
||||
nested:
|
||||
title: Titel
|
3
src/test/resources/single/yaml/en.yaml
Normal file
3
src/test/resources/single/yaml/en.yaml
Normal file
@ -0,0 +1,3 @@
|
||||
title: Title
|
||||
nested:
|
||||
title: Title
|
Loading…
x
Reference in New Issue
Block a user