Merge pull request #193 from marhali/next

Next
This commit is contained in:
Marcel 2022-10-28 23:13:16 +02:00 committed by GitHub
commit da655d91ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 361 additions and 22 deletions

View File

@ -3,6 +3,15 @@
# 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

View File

@ -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

View File

@ -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();

View File

@ -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()));
}

View File

@ -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());
}
/**

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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();
}
});
}

View File

@ -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;
}

View File

@ -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;
}
}
}

View File

@ -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);

View File

@ -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

View File

@ -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)$"));
}
}

View 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));
}
}
}

View 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;
}
}

View File

@ -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)
);
}
}

View File

@ -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)
);
}
}

View File

@ -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)
);
}
}

View File

@ -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)
);
}
}

View File

@ -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() {

View File

@ -0,0 +1,11 @@
{
"title": "Titel",
"number": 187,
"object": {
"title": "Titel"
},
"array": [
"element1",
"element2"
]
}

View File

@ -0,0 +1,11 @@
{
"title": "Title",
"number": -187,
"object": {
"title": "Title"
},
"array": [
"item1",
"item2"
]
}

View File

@ -0,0 +1,12 @@
{
"title": "Titel",
"number": 187,
"hex": 0x187,
"object": {
"title": "Titel",
},
"array": [
"element1",
"element2",
],
}

View File

@ -0,0 +1,12 @@
{
"title": "Title",
"number": -187,
"hex": -0x187,
"object": {
"title": "Title",
},
"array": [
"item1",
"item2",
],
}

View File

@ -0,0 +1,2 @@
breakLine=eins\nzwei
title=Titel

View File

@ -0,0 +1,2 @@
breakLine=first\nsecond
title=Title

View File

@ -0,0 +1,3 @@
title: Titel
nested:
title: Titel

View File

@ -0,0 +1,3 @@
title: Title
nested:
title: Title