diff --git a/src/main/java/de/marhali/easyi18n/io/translator/PropertiesTranslatorIO.java b/src/main/java/de/marhali/easyi18n/io/translator/PropertiesTranslatorIO.java new file mode 100644 index 0000000..c03cb40 --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/io/translator/PropertiesTranslatorIO.java @@ -0,0 +1,123 @@ +package de.marhali.easyi18n.io.translator; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; + +import de.marhali.easyi18n.data.LocalizedNode; +import de.marhali.easyi18n.data.Translations; +import de.marhali.easyi18n.util.TranslationsUtil; + +import org.jetbrains.annotations.NotNull; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.*; +import java.util.function.Consumer; + +/** + * Implementation for properties translation files. + * @author marhali + */ +public class PropertiesTranslatorIO implements TranslatorIO { + + public static final String FILE_EXTENSION = "properties"; + + @Override + public void read(@NotNull String directoryPath, @NotNull Consumer 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[] files = directory.getChildren(); + + List locales = new ArrayList<>(); + LocalizedNode nodes = new LocalizedNode(LocalizedNode.ROOT_KEY, new ArrayList<>()); + + try { + for (VirtualFile file : files) { + locales.add(file.getNameWithoutExtension()); + Properties properties = new Properties(); + properties.load(new InputStreamReader(file.getInputStream()));; + readProperties(file.getNameWithoutExtension(), properties, nodes); + } + + 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 callback) { + ApplicationManager.getApplication().runWriteAction(() -> { + try { + for(String locale : translations.getLocales()) { + Properties properties = new Properties(); + writeProperties(locale, properties, translations.getNodes(), ""); + + String fullPath = directoryPath + "/" + locale + "." + FILE_EXTENSION; + VirtualFile file = LocalFileSystem.getInstance().findFileByIoFile(new File(fullPath)); + + ByteArrayOutputStream content = new ByteArrayOutputStream(); + properties.store(content, "I18n " + locale + " keys"); + file.setBinaryContent(content.toByteArray()); + } + + // Successfully saved + callback.accept(true); + + } catch(IOException e) { + e.printStackTrace(); + callback.accept(false); + } + }); + } + + private void writeProperties(String locale, Properties props, LocalizedNode node, String parentPath) { + if(node.isLeaf() && !node.getKey().equals(LocalizedNode.ROOT_KEY)) { + if(node.getValue().get(locale) != null) { // Translation is defined - track it + props.setProperty(parentPath, node.getValue().get(locale)); + } + + } else { + for(LocalizedNode children : node.getChildren()) { + writeProperties(locale, props, children, + parentPath + (parentPath.isEmpty() ? "" : ".") + children.getKey()); + } + } + } + + private void readProperties(String locale, Properties props, LocalizedNode parent) { + props.forEach((key, value) -> { + List sections = TranslationsUtil.getSections(String.valueOf(key)); + + LocalizedNode node = parent; + + for (String section : sections) { + LocalizedNode subNode = node.getChildren(section); + + if(subNode == null) { + subNode = new LocalizedNode(section, new ArrayList<>()); + node.addChildren(subNode); + } + + node = subNode; + } + + Map messages = node.getValue(); + messages.put(locale, String.valueOf(value)); + node.setValue(messages); + }); + } +} \ No newline at end of file diff --git a/src/main/java/de/marhali/easyi18n/util/IOUtil.java b/src/main/java/de/marhali/easyi18n/util/IOUtil.java index 0ad9162..ff8a318 100644 --- a/src/main/java/de/marhali/easyi18n/util/IOUtil.java +++ b/src/main/java/de/marhali/easyi18n/util/IOUtil.java @@ -3,15 +3,27 @@ package de.marhali.easyi18n.util; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import de.marhali.easyi18n.io.translator.JsonTranslatorIO; +import de.marhali.easyi18n.io.translator.PropertiesTranslatorIO; import de.marhali.easyi18n.io.translator.TranslatorIO; +import org.jetbrains.annotations.NotNull; + import java.io.File; import java.util.Arrays; import java.util.Optional; +/** + * IO operations utility. + * @author marhali + */ public class IOUtil { - public static TranslatorIO determineFormat(String directoryPath) { + /** + * Determines the {@link TranslatorIO} which should be used for the specified directoryPath + * @param directoryPath The full path to the parent directory which holds the translation files + * @return IO handler to use for file operations + */ + public static TranslatorIO determineFormat(@NotNull String directoryPath) { VirtualFile directory = LocalFileSystem.getInstance().findFileByIoFile(new File(directoryPath)); if(directory == null || directory.getChildren() == null) { @@ -21,7 +33,7 @@ public class IOUtil { Optional any = Arrays.stream(directory.getChildren()).findAny(); if(!any.isPresent()) { - throw new IllegalStateException("Could not determine format"); + throw new IllegalStateException("Could not determine i18n format. At least one locale file must be defined"); } switch (any.get().getFileType().getDefaultExtension().toLowerCase()) { @@ -29,10 +41,10 @@ public class IOUtil { return new JsonTranslatorIO(); case "properties": - throw new UnsupportedOperationException(); + return new PropertiesTranslatorIO(); default: - throw new UnsupportedOperationException("Unsupported format: " + + throw new UnsupportedOperationException("Unsupported i18n locale file format: " + any.get().getFileType().getDefaultExtension()); } }