consider subdirectories for modularized translation files
Resolves #124
This commit is contained in:
parent
8dc66e1250
commit
c03d97f3e8
@ -7,6 +7,7 @@
|
|||||||
- Duplicate translation values filter
|
- Duplicate translation values filter
|
||||||
- Indicate translations with duplicated values yellow
|
- Indicate translations with duplicated values yellow
|
||||||
- Multiple translation filters can be used together
|
- Multiple translation filters can be used together
|
||||||
|
- Option to consider subdirectories for modularized translation files
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Reengineered how translation filters are applied internally
|
- Reengineered how translation filters are applied internally
|
||||||
|
@ -6,8 +6,8 @@ import com.intellij.openapi.vfs.VirtualFile;
|
|||||||
import de.marhali.easyi18n.io.parser.ParserStrategyType;
|
import de.marhali.easyi18n.io.parser.ParserStrategyType;
|
||||||
import de.marhali.easyi18n.model.TranslationData;
|
import de.marhali.easyi18n.model.TranslationData;
|
||||||
import de.marhali.easyi18n.model.TranslationFile;
|
import de.marhali.easyi18n.model.TranslationFile;
|
||||||
|
|
||||||
import de.marhali.easyi18n.settings.ProjectSettings;
|
import de.marhali.easyi18n.settings.ProjectSettings;
|
||||||
|
|
||||||
import org.apache.commons.io.FilenameUtils;
|
import org.apache.commons.io.FilenameUtils;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
@ -71,4 +71,14 @@ public abstract class FolderStrategy {
|
|||||||
|
|
||||||
return Objects.requireNonNull(vf);
|
return Objects.requireNonNull(vf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether a given file or directory exists
|
||||||
|
* @param parent Parent path
|
||||||
|
* @param child File / Directory name
|
||||||
|
* @return true if file is existing otherwise false
|
||||||
|
*/
|
||||||
|
protected boolean exists(@NotNull String parent, @NotNull String child) {
|
||||||
|
return LocalFileSystem.getInstance().findFileByIoFile(new File(parent, child)) != null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,10 @@ package de.marhali.easyi18n.io.folder;
|
|||||||
import com.intellij.openapi.vfs.VirtualFile;
|
import com.intellij.openapi.vfs.VirtualFile;
|
||||||
|
|
||||||
import de.marhali.easyi18n.io.parser.ParserStrategyType;
|
import de.marhali.easyi18n.io.parser.ParserStrategyType;
|
||||||
|
import de.marhali.easyi18n.model.KeyPath;
|
||||||
import de.marhali.easyi18n.model.TranslationData;
|
import de.marhali.easyi18n.model.TranslationData;
|
||||||
import de.marhali.easyi18n.model.TranslationFile;
|
import de.marhali.easyi18n.model.TranslationFile;
|
||||||
|
import de.marhali.easyi18n.model.TranslationNode;
|
||||||
import de.marhali.easyi18n.settings.ProjectSettings;
|
import de.marhali.easyi18n.settings.ProjectSettings;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@ -12,6 +14,7 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Modularized translation folder strategy by locale.
|
* Modularized translation folder strategy by locale.
|
||||||
@ -31,12 +34,26 @@ public class ModularLocaleFolderStrategy extends FolderStrategy {
|
|||||||
for(VirtualFile localeModuleDir : localesDirectory.getChildren()) {
|
for(VirtualFile localeModuleDir : localesDirectory.getChildren()) {
|
||||||
if(localeModuleDir.isDirectory()) {
|
if(localeModuleDir.isDirectory()) {
|
||||||
String locale = localeModuleDir.getNameWithoutExtension();
|
String locale = localeModuleDir.getNameWithoutExtension();
|
||||||
|
files.addAll(findNamespaceFiles(locale, new KeyPath(), localeModuleDir));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<TranslationFile> findNamespaceFiles(@NotNull String locale, @NotNull KeyPath ns, @NotNull VirtualFile dir) {
|
||||||
|
List<TranslationFile> files = new ArrayList<>();
|
||||||
|
|
||||||
|
for(VirtualFile namespaceFile : dir.getChildren()) {
|
||||||
|
if(namespaceFile.isDirectory()) {
|
||||||
|
if(settings.isIncludeSubDirs()) {
|
||||||
|
files.addAll(findNamespaceFiles(locale, new KeyPath(ns, namespaceFile.getName()), namespaceFile));
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
for(VirtualFile namespaceFile : localeModuleDir.getChildren()) {
|
|
||||||
if(super.isFileRelevant(namespaceFile)) {
|
if(super.isFileRelevant(namespaceFile)) {
|
||||||
files.add(new TranslationFile(namespaceFile, locale, namespaceFile.getNameWithoutExtension()));
|
files.add(new TranslationFile(namespaceFile, locale, new KeyPath(ns, namespaceFile.getNameWithoutExtension())));
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,12 +68,28 @@ public class ModularLocaleFolderStrategy extends FolderStrategy {
|
|||||||
List<TranslationFile> files = new ArrayList<>();
|
List<TranslationFile> files = new ArrayList<>();
|
||||||
|
|
||||||
for(String locale : data.getLocales()) {
|
for(String locale : data.getLocales()) {
|
||||||
for(String namespace : data.getRootNode().getChildren().keySet()) {
|
files.addAll(this.createNamespaceFiles(localesPath, locale, new KeyPath(), type, data.getRootNode()));
|
||||||
VirtualFile vf = super.constructFile(localesPath + "/" + locale,
|
|
||||||
namespace + "." + type.getFileExtension());
|
|
||||||
|
|
||||||
files.add(new TranslationFile(vf, locale, namespace));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<TranslationFile> createNamespaceFiles(
|
||||||
|
String localesPath, String locale, KeyPath path,
|
||||||
|
ParserStrategyType type, TranslationNode node) throws IOException {
|
||||||
|
|
||||||
|
List<TranslationFile> files = new ArrayList<>();
|
||||||
|
|
||||||
|
for(Map.Entry<String, TranslationNode> entry : node.getChildren().entrySet()) {
|
||||||
|
String parentPath = localesPath + "/" + locale + "/" + String.join("/", path);
|
||||||
|
|
||||||
|
if(super.exists(parentPath, entry.getKey())) { // Is directory - includeSubDirs
|
||||||
|
files.addAll(createNamespaceFiles(localesPath, locale, new KeyPath(path, entry.getKey()), type, entry.getValue()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualFile vf = super.constructFile(parentPath, entry.getKey() + "." + type.getFileExtension());
|
||||||
|
files.add(new TranslationFile(vf, locale, new KeyPath(path, entry.getKey())));
|
||||||
}
|
}
|
||||||
|
|
||||||
return files;
|
return files;
|
||||||
|
@ -3,8 +3,10 @@ package de.marhali.easyi18n.io.folder;
|
|||||||
import com.intellij.openapi.vfs.VirtualFile;
|
import com.intellij.openapi.vfs.VirtualFile;
|
||||||
|
|
||||||
import de.marhali.easyi18n.io.parser.ParserStrategyType;
|
import de.marhali.easyi18n.io.parser.ParserStrategyType;
|
||||||
|
import de.marhali.easyi18n.model.KeyPath;
|
||||||
import de.marhali.easyi18n.model.TranslationData;
|
import de.marhali.easyi18n.model.TranslationData;
|
||||||
import de.marhali.easyi18n.model.TranslationFile;
|
import de.marhali.easyi18n.model.TranslationFile;
|
||||||
|
import de.marhali.easyi18n.model.TranslationNode;
|
||||||
import de.marhali.easyi18n.settings.ProjectSettings;
|
import de.marhali.easyi18n.settings.ProjectSettings;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@ -12,6 +14,8 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Modular translation folder strategy by namespace.
|
* Modular translation folder strategy by namespace.
|
||||||
@ -26,17 +30,22 @@ public class ModularNamespaceFolderStrategy extends FolderStrategy {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull List<TranslationFile> analyzeFolderStructure(@NotNull VirtualFile localesDirectory) {
|
public @NotNull List<TranslationFile> analyzeFolderStructure(@NotNull VirtualFile localesDirectory) {
|
||||||
|
return new ArrayList<>(findLocaleFiles(new KeyPath(), localesDirectory));
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<TranslationFile> findLocaleFiles(KeyPath ns, VirtualFile dir) {
|
||||||
List<TranslationFile> files = new ArrayList<>();
|
List<TranslationFile> files = new ArrayList<>();
|
||||||
|
|
||||||
for(VirtualFile namespaceModuleDir : localesDirectory.getChildren()) {
|
for (VirtualFile localeFile : dir.getChildren()) {
|
||||||
if(namespaceModuleDir.isDirectory()) {
|
if(localeFile.isDirectory()) {
|
||||||
String namespace = namespaceModuleDir.getNameWithoutExtension();
|
if(settings.isIncludeSubDirs()) {
|
||||||
|
files.addAll(findLocaleFiles(new KeyPath(ns, localeFile.getName()), localeFile));
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
for(VirtualFile localeFile : namespaceModuleDir.getChildren()) {
|
|
||||||
if(super.isFileRelevant(localeFile)) {
|
if(super.isFileRelevant(localeFile)) {
|
||||||
files.add(new TranslationFile(localeFile, localeFile.getNameWithoutExtension(), namespace));
|
files.add(new TranslationFile(localeFile, localeFile.getNameWithoutExtension(), ns));
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,14 +57,24 @@ public class ModularNamespaceFolderStrategy extends FolderStrategy {
|
|||||||
@NotNull String localesPath, @NotNull ParserStrategyType type,
|
@NotNull String localesPath, @NotNull ParserStrategyType type,
|
||||||
@NotNull TranslationData data) throws IOException {
|
@NotNull TranslationData data) throws IOException {
|
||||||
|
|
||||||
|
return new ArrayList<>(this.createLocaleFiles(
|
||||||
|
localesPath, data.getLocales(), new KeyPath(), type, data.getRootNode()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<TranslationFile> createLocaleFiles(String localesPath, Set<String> locales, KeyPath path, ParserStrategyType type, TranslationNode node) throws IOException {
|
||||||
List<TranslationFile> files = new ArrayList<>();
|
List<TranslationFile> files = new ArrayList<>();
|
||||||
|
|
||||||
for(String namespace : data.getRootNode().getChildren().keySet()) {
|
for (Map.Entry<String, TranslationNode> entry : node.getChildren().entrySet()) {
|
||||||
for(String locale : data.getLocales()) {
|
String parentPath = localesPath + "/" + String.join("/", path);
|
||||||
VirtualFile vf = super.constructFile(localesPath + "/" + namespace,
|
|
||||||
locale + "." + type.getFileExtension());
|
|
||||||
|
|
||||||
files.add(new TranslationFile(vf, locale, namespace));
|
if(super.exists(parentPath, entry.getKey())) { // Is directory - includeSubDirs
|
||||||
|
files.addAll(createLocaleFiles(localesPath, locales, new KeyPath(path, entry.getKey()), type, entry.getValue()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String locale : locales) {
|
||||||
|
VirtualFile vf = super.constructFile(parentPath, locale + "." + type.getFileExtension());
|
||||||
|
files.add(new TranslationFile(vf, locale, path));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,15 +46,7 @@ public abstract class ParserStrategy {
|
|||||||
TranslationNode targetNode = data.getRootNode();
|
TranslationNode targetNode = data.getRootNode();
|
||||||
|
|
||||||
if(file.getNamespace() != null) {
|
if(file.getNamespace() != null) {
|
||||||
String moduleName = file.getNamespace();
|
targetNode = data.getOrCreateNoe(file.getNamespace());
|
||||||
TranslationNode moduleNode = data.getNode(new KeyPath(moduleName));
|
|
||||||
|
|
||||||
if(moduleNode == null) {
|
|
||||||
moduleNode = new TranslationNode(this.settings.isSorting());
|
|
||||||
data.getRootNode().setChildren(moduleName, moduleNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
targetNode = moduleNode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return targetNode;
|
return targetNode;
|
||||||
|
@ -89,6 +89,20 @@ public class TranslationData {
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public @NotNull TranslationNode getOrCreateNoe(@NotNull KeyPath fullPath) {
|
||||||
|
TranslationNode node = this.rootNode;
|
||||||
|
|
||||||
|
if(fullPath.isEmpty()) { // Return root node if empty path was supplied
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(String section : fullPath) {
|
||||||
|
node = node.getOrCreateChildren(section);
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param fullPath Absolute translation key path
|
* @param fullPath Absolute translation key path
|
||||||
* @return Found translation. Can be null if path is empty or is not a leaf element
|
* @return Found translation. Can be null if path is empty or is not a leaf element
|
||||||
|
@ -13,9 +13,9 @@ public class TranslationFile {
|
|||||||
|
|
||||||
private final @NotNull VirtualFile virtualFile;
|
private final @NotNull VirtualFile virtualFile;
|
||||||
private final @NotNull String locale;
|
private final @NotNull String locale;
|
||||||
private final @Nullable String namespace;
|
private final @Nullable KeyPath namespace;
|
||||||
|
|
||||||
public TranslationFile(@NotNull VirtualFile virtualFile, @NotNull String locale, @Nullable String namespace) {
|
public TranslationFile(@NotNull VirtualFile virtualFile, @NotNull String locale, @Nullable KeyPath namespace) {
|
||||||
this.virtualFile = virtualFile;
|
this.virtualFile = virtualFile;
|
||||||
this.locale = locale;
|
this.locale = locale;
|
||||||
this.namespace = namespace;
|
this.namespace = namespace;
|
||||||
@ -29,7 +29,7 @@ public class TranslationFile {
|
|||||||
return locale;
|
return locale;
|
||||||
}
|
}
|
||||||
|
|
||||||
public @Nullable String getNamespace() {
|
public @Nullable KeyPath getNamespace() {
|
||||||
return namespace;
|
return namespace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,6 +31,8 @@ settings.resource.folder.tooltip=What is the folder structure of your translatio
|
|||||||
settings.resource.parser.items=JSON;JSON5;YAML;YML;Properties;ARB
|
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.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 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
|
settings.resource.sorting.title=Sort translation keys alphabetically
|
||||||
settings.resource.sorting.tooltip=Sorts all translation keys alphabetically. If disabled, the original key-order in the files is kept.
|
settings.resource.sorting.tooltip=Sorts all translation keys alphabetically. If disabled, the original key-order in the files is kept.
|
||||||
# Editor Configuration
|
# Editor Configuration
|
||||||
|
Loading…
x
Reference in New Issue
Block a user