Add basic support for modularized / splitted json files | Fixes Issue #4

This commit is contained in:
Marcel Haßlinger 2021-04-24 21:16:42 +02:00
parent 895bdbbe7f
commit 5f1d96d91d
4 changed files with 214 additions and 59 deletions

View File

@ -8,6 +8,7 @@ import com.intellij.openapi.vfs.VirtualFile;
import de.marhali.easyi18n.io.TranslatorIO;
import de.marhali.easyi18n.model.LocalizedNode;
import de.marhali.easyi18n.model.Translations;
import de.marhali.easyi18n.util.JsonUtil;
import org.jetbrains.annotations.NotNull;
@ -44,8 +45,11 @@ public class JsonTranslatorIO implements TranslatorIO {
try {
for(VirtualFile file : files) {
locales.add(file.getNameWithoutExtension());
JsonObject tree = JsonParser.parseReader(new InputStreamReader(file.getInputStream(), file.getCharset())).getAsJsonObject();
readTree(file.getNameWithoutExtension(), tree, nodes);
JsonObject tree = JsonParser.parseReader(new InputStreamReader(file.getInputStream(),
file.getCharset())).getAsJsonObject();
JsonUtil.readTree(file.getNameWithoutExtension(), tree, nodes);
}
callback.accept(new Translations(locales, nodes));
@ -65,13 +69,16 @@ public class JsonTranslatorIO implements TranslatorIO {
try {
for(String locale : translations.getLocales()) {
JsonObject content = new JsonObject();
writeTree(locale, content, translations.getNodes());
//JsonElement content = writeTree(locale, new JsonObject(), translations.getNodes());
JsonUtil.writeTree(locale, content, translations.getNodes());
String fullPath = directoryPath + "/" + locale + "." + FILE_EXTENSION;
VirtualFile file = LocalFileSystem.getInstance().findFileByIoFile(new File(fullPath));
File file = new File(fullPath);
boolean created = file.createNewFile();
file.setBinaryContent(gson.toJson(content).getBytes(file.getCharset()));
VirtualFile vf = created ? LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file)
: LocalFileSystem.getInstance().findFileByIoFile(file);
vf.setBinaryContent(gson.toJson(content).getBytes(vf.getCharset()));
}
// Successfully saved
@ -83,57 +90,4 @@ public class JsonTranslatorIO implements TranslatorIO {
}
});
}
private void writeTree(String locale, JsonObject parent, LocalizedNode node) {
if(node.isLeaf() && !node.getKey().equals(LocalizedNode.ROOT_KEY)) {
if(node.getValue().get(locale) != null) {
parent.add(node.getKey(), new JsonPrimitive(node.getValue().get(locale)));
}
} else {
for(LocalizedNode children : node.getChildren()) {
if(children.isLeaf()) {
writeTree(locale, parent, children);
} else {
JsonObject childrenJson = new JsonObject();
writeTree(locale, childrenJson, children);
if(childrenJson.size() > 0) {
parent.add(children.getKey(), childrenJson);
}
}
}
}
}
private void readTree(String locale, JsonObject json, LocalizedNode data) {
for(Map.Entry<String, JsonElement> entry : json.entrySet()) {
String key = entry.getKey();
try {
// Try to go one level deeper
JsonObject childObject = entry.getValue().getAsJsonObject();
LocalizedNode childrenNode = data.getChildren(key);
if(childrenNode == null) {
childrenNode = new LocalizedNode(key, new ArrayList<>());
data.addChildren(childrenNode);
}
readTree(locale, childObject, childrenNode);
} catch(IllegalStateException e) { // Reached end for this node
LocalizedNode leafNode = data.getChildren(key);
if(leafNode == null) {
leafNode = new LocalizedNode(key, new HashMap<>());
data.addChildren(leafNode);
}
Map<String, String> messages = leafNode.getValue();
messages.put(locale, entry.getValue().getAsString());
leafNode.setValue(messages);
}
}
}
}

View File

@ -0,0 +1,112 @@
package de.marhali.easyi18n.io.implementation;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import de.marhali.easyi18n.io.TranslatorIO;
import de.marhali.easyi18n.model.LocalizedNode;
import de.marhali.easyi18n.model.Translations;
import de.marhali.easyi18n.util.JsonUtil;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
/**
* IO operations for splitted / modularized json files. Each locale can have multiple translation files.
* @author marhali
*/
public class ModularizedJsonTranslatorIO implements TranslatorIO {
private static final String FILE_EXTENSION = "json";
@Override
public void read(@NotNull String directoryPath, @NotNull Consumer<Translations> callback) {
ApplicationManager.getApplication().saveAll(); // Save opened files (required if new locales were added)
ApplicationManager.getApplication().runReadAction(() -> {
VirtualFile directory = LocalFileSystem.getInstance().findFileByIoFile(new File(directoryPath));
if(directory == null || directory.getChildren() == null) {
throw new IllegalArgumentException("Specified folder is invalid (" + directoryPath + ")");
}
VirtualFile[] localeDirectories = directory.getChildren();
List<String> locales = new ArrayList<>();
LocalizedNode nodes = new LocalizedNode(LocalizedNode.ROOT_KEY, new ArrayList<>());
try {
for(VirtualFile localeDir : localeDirectories) {
String locale = localeDir.getName();
locales.add(locale);
// Read all json modules
for(VirtualFile module : localeDir.getChildren()) {
JsonObject tree = JsonParser.parseReader(new InputStreamReader(module.getInputStream(),
module.getCharset())).getAsJsonObject();
String moduleName = module.getNameWithoutExtension();
LocalizedNode moduleNode = nodes.getChildren(moduleName);
if(moduleNode == null) { // Create module / sub node
moduleNode = new LocalizedNode(moduleName, new ArrayList<>());
nodes.addChildren(moduleNode);
}
JsonUtil.readTree(locale, tree, moduleNode);
}
}
callback.accept(new Translations(locales, nodes));
} catch(IOException e) {
e.printStackTrace();
callback.accept(null);
}
});
}
@Override
public void save(@NotNull Translations translations, @NotNull String directoryPath, @NotNull Consumer<Boolean> callback) {
Gson gson = new GsonBuilder().setPrettyPrinting().create();
ApplicationManager.getApplication().runWriteAction(() -> {
try {
for(String locale : translations.getLocales()) {
// Use top level children as modules
for (LocalizedNode module : translations.getNodes().getChildren()) {
JsonObject content = new JsonObject();
JsonUtil.writeTree(locale, content, module);
String fullPath = directoryPath + "/" + locale + "/" + module.getKey() + "." + FILE_EXTENSION;
File file = new File(fullPath);
boolean created = file.createNewFile();
VirtualFile vf = created ? LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file)
: LocalFileSystem.getInstance().findFileByIoFile(file);
vf.setBinaryContent(gson.toJson(content).getBytes(vf.getCharset()));
}
}
// Successfully saved
callback.accept(true);
} catch(IOException e) {
e.printStackTrace();
callback.accept(false);
}
});
}
}

View File

@ -3,6 +3,7 @@ package de.marhali.easyi18n.util;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import de.marhali.easyi18n.io.implementation.JsonTranslatorIO;
import de.marhali.easyi18n.io.implementation.ModularizedJsonTranslatorIO;
import de.marhali.easyi18n.io.implementation.PropertiesTranslatorIO;
import de.marhali.easyi18n.io.TranslatorIO;
@ -36,6 +37,11 @@ public class IOUtil {
throw new IllegalStateException("Could not determine i18n format. At least one locale file must be defined");
}
// Split files - Should be always JSON
if(any.get().isDirectory()) {
return new ModularizedJsonTranslatorIO();
}
switch (any.get().getFileType().getDefaultExtension().toLowerCase()) {
case "json":
return new JsonTranslatorIO();

View File

@ -0,0 +1,83 @@
package de.marhali.easyi18n.util;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import de.marhali.easyi18n.model.LocalizedNode;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
/**
* Json tree utilities for writing and reading {@link LocalizedNode}'s
* @author marhali
*/
public class JsonUtil {
/**
* Creates a {@link JsonObject} based from an {@link LocalizedNode}
* @param locale Current locale
* @param parent Parent json. Can be an entire json document
* @param node The node instance
*/
public static void writeTree(String locale, JsonObject parent, LocalizedNode node) {
if(node.isLeaf() && !node.getKey().equals(LocalizedNode.ROOT_KEY)) {
if(node.getValue().get(locale) != null) {
parent.add(node.getKey(), new JsonPrimitive(node.getValue().get(locale)));
}
} else {
for(LocalizedNode children : node.getChildren()) {
if(children.isLeaf()) {
writeTree(locale, parent, children);
} else {
JsonObject childrenJson = new JsonObject();
writeTree(locale, childrenJson, children);
if(childrenJson.size() > 0) {
parent.add(children.getKey(), childrenJson);
}
}
}
}
}
/**
* Reads a {@link JsonObject} and writes the tree into the provided {@link LocalizedNode}
* @param locale Current locale
* @param json Json to read
* @param data Node. Can be a root node
*/
public static void readTree(String locale, JsonObject json, LocalizedNode data) {
for(Map.Entry<String, JsonElement> entry : json.entrySet()) {
String key = entry.getKey();
try {
// Try to go one level deeper
JsonObject childObject = entry.getValue().getAsJsonObject();
LocalizedNode childrenNode = data.getChildren(key);
if(childrenNode == null) {
childrenNode = new LocalizedNode(key, new ArrayList<>());
data.addChildren(childrenNode);
}
readTree(locale, childObject, childrenNode);
} catch(IllegalStateException e) { // Reached end for this node
LocalizedNode leafNode = data.getChildren(key);
if(leafNode == null) {
leafNode = new LocalizedNode(key, new HashMap<>());
data.addChildren(leafNode);
}
Map<String, String> messages = leafNode.getValue();
messages.put(locale, entry.getValue().getAsString());
leafNode.setValue(messages);
}
}
}
}