Merge pull request #11 from marhali/dev
Properly escape/unescape strings and sort properties files
This commit is contained in:
commit
11fd99e7f4
@ -3,6 +3,12 @@
|
||||
# easy-i18n Changelog
|
||||
|
||||
## [Unreleased]
|
||||
### Added
|
||||
- Sorting for properties files
|
||||
|
||||
### Fixed
|
||||
- Unexpected character escaping for json/properties files / issue #10
|
||||
|
||||
## [1.1.1]
|
||||
### Added
|
||||
- Support for IntelliJ 2021.1
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
pluginGroup = de.marhali.easyi18n
|
||||
pluginName = easy-i18n
|
||||
pluginVersion = 1.1.1
|
||||
pluginVersion = 1.2.0
|
||||
pluginSinceBuild = 202
|
||||
pluginUntilBuild = 211.*
|
||||
# Plugin Verifier integration -> https://github.com/JetBrains/gradle-intellij-plugin#plugin-verifier-dsl
|
||||
|
@ -27,6 +27,7 @@ import java.util.function.Consumer;
|
||||
public class JsonTranslatorIO implements TranslatorIO {
|
||||
|
||||
private static final String FILE_EXTENSION = "json";
|
||||
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
|
||||
|
||||
@Override
|
||||
public void read(@NotNull Project project, @NotNull String directoryPath, @NotNull Consumer<Translations> callback) {
|
||||
@ -53,8 +54,8 @@ public class JsonTranslatorIO implements TranslatorIO {
|
||||
|
||||
locales.add(file.getNameWithoutExtension());
|
||||
|
||||
JsonObject tree = JsonParser.parseReader(new InputStreamReader(file.getInputStream(),
|
||||
file.getCharset())).getAsJsonObject();
|
||||
JsonObject tree = GSON.fromJson(new InputStreamReader(file.getInputStream(),
|
||||
file.getCharset()), JsonObject.class);
|
||||
|
||||
JsonUtil.readTree(file.getNameWithoutExtension(), tree, nodes);
|
||||
}
|
||||
@ -71,9 +72,6 @@ public class JsonTranslatorIO implements TranslatorIO {
|
||||
@Override
|
||||
public void save(@NotNull Project project, @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()) {
|
||||
@ -87,7 +85,7 @@ public class JsonTranslatorIO implements TranslatorIO {
|
||||
VirtualFile vf = created ? LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file)
|
||||
: LocalFileSystem.getInstance().findFileByIoFile(file);
|
||||
|
||||
vf.setBinaryContent(gson.toJson(content).getBytes(vf.getCharset()));
|
||||
vf.setBinaryContent(GSON.toJson(content).getBytes(vf.getCharset()));
|
||||
}
|
||||
|
||||
// Successfully saved
|
||||
|
@ -9,8 +9,11 @@ import de.marhali.easyi18n.io.TranslatorIO;
|
||||
import de.marhali.easyi18n.model.LocalizedNode;
|
||||
import de.marhali.easyi18n.model.Translations;
|
||||
import de.marhali.easyi18n.util.IOUtil;
|
||||
import de.marhali.easyi18n.util.SortedProperties;
|
||||
import de.marhali.easyi18n.util.StringUtil;
|
||||
import de.marhali.easyi18n.util.TranslationsUtil;
|
||||
|
||||
import org.apache.commons.lang.StringEscapeUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.*;
|
||||
@ -49,7 +52,7 @@ public class PropertiesTranslatorIO implements TranslatorIO {
|
||||
}
|
||||
|
||||
locales.add(file.getNameWithoutExtension());
|
||||
Properties properties = new Properties();
|
||||
SortedProperties properties = new SortedProperties();
|
||||
properties.load(new InputStreamReader(file.getInputStream(), file.getCharset()));
|
||||
readProperties(file.getNameWithoutExtension(), properties, nodes);
|
||||
}
|
||||
@ -70,7 +73,7 @@ public class PropertiesTranslatorIO implements TranslatorIO {
|
||||
ApplicationManager.getApplication().runWriteAction(() -> {
|
||||
try {
|
||||
for(String locale : translations.getLocales()) {
|
||||
Properties properties = new Properties();
|
||||
SortedProperties properties = new SortedProperties();
|
||||
writeProperties(locale, properties, translations.getNodes(), "");
|
||||
|
||||
String fullPath = directoryPath + "/" + locale + "." + FILE_EXTENSION;
|
||||
@ -95,7 +98,8 @@ public class PropertiesTranslatorIO implements TranslatorIO {
|
||||
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));
|
||||
String value = StringEscapeUtils.unescapeJava(node.getValue().get(locale));
|
||||
props.setProperty(parentPath, value);
|
||||
}
|
||||
|
||||
} else {
|
||||
@ -124,7 +128,8 @@ public class PropertiesTranslatorIO implements TranslatorIO {
|
||||
}
|
||||
|
||||
Map<String, String> messages = node.getValue();
|
||||
messages.put(locale, String.valueOf(value));
|
||||
String escapedValue = StringUtil.escapeControls(String.valueOf(value), true);
|
||||
messages.put(locale, escapedValue);
|
||||
node.setValue(messages);
|
||||
});
|
||||
}
|
||||
|
@ -6,6 +6,8 @@ import com.google.gson.JsonPrimitive;
|
||||
|
||||
import de.marhali.easyi18n.model.LocalizedNode;
|
||||
|
||||
import org.apache.commons.lang.StringEscapeUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@ -25,7 +27,8 @@ public class JsonUtil {
|
||||
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)));
|
||||
String value = StringEscapeUtils.unescapeJava(node.getValue().get(locale));
|
||||
parent.add(node.getKey(), new JsonPrimitive(value));
|
||||
}
|
||||
|
||||
} else {
|
||||
@ -75,7 +78,8 @@ public class JsonUtil {
|
||||
}
|
||||
|
||||
Map<String, String> messages = leafNode.getValue();
|
||||
messages.put(locale, entry.getValue().getAsString());
|
||||
String value = StringUtil.escapeControls(entry.getValue().getAsString(), true);
|
||||
messages.put(locale, value);
|
||||
leafNode.setValue(messages);
|
||||
}
|
||||
}
|
||||
|
31
src/main/java/de/marhali/easyi18n/util/SortedProperties.java
Normal file
31
src/main/java/de/marhali/easyi18n/util/SortedProperties.java
Normal file
@ -0,0 +1,31 @@
|
||||
package de.marhali.easyi18n.util;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Applies sorting to {@link Properties} files.
|
||||
* @author marhali
|
||||
*/
|
||||
public class SortedProperties extends Properties {
|
||||
|
||||
@Override
|
||||
public Set<Object> keySet() {
|
||||
return Collections.unmodifiableSet(new TreeSet<>(super.keySet()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Map.Entry<Object, Object>> entrySet() {
|
||||
TreeMap<Object, Object> sorted = new TreeMap<>();
|
||||
|
||||
for(Object key : super.keySet()) {
|
||||
sorted.put(key, get(key));
|
||||
}
|
||||
|
||||
return sorted.entrySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Enumeration<Object> keys() {
|
||||
return Collections.enumeration(new TreeSet<>(super.keySet()));
|
||||
}
|
||||
}
|
91
src/main/java/de/marhali/easyi18n/util/StringUtil.java
Normal file
91
src/main/java/de/marhali/easyi18n/util/StringUtil.java
Normal file
@ -0,0 +1,91 @@
|
||||
package de.marhali.easyi18n.util;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.StringWriter;
|
||||
|
||||
/**
|
||||
* String utilities
|
||||
* @author marhali, Apache Commons
|
||||
*/
|
||||
public class StringUtil {
|
||||
|
||||
/**
|
||||
* Escapes control characters for the given input string.
|
||||
* Inspired by Apache Commons (see {@link org.apache.commons.lang.StringEscapeUtils}
|
||||
* @param input The input string
|
||||
* @param skipStrings Should every string literal indication ("", '') be skipped? (Needed e.g. for json)
|
||||
* @return Escaped string
|
||||
*/
|
||||
public static @NotNull String escapeControls(@NotNull String input, boolean skipStrings) {
|
||||
int length = input.length();
|
||||
StringWriter out = new StringWriter(length * 2);
|
||||
|
||||
for(int i = 0; i < length; i++) {
|
||||
char ch = input.charAt(i);
|
||||
|
||||
if(ch < ' ') {
|
||||
switch(ch) {
|
||||
case '\b':
|
||||
out.write(92);
|
||||
out.write(98);
|
||||
break;
|
||||
case '\t':
|
||||
out.write(92);
|
||||
out.write(116);
|
||||
break;
|
||||
case '\n':
|
||||
out.write(92);
|
||||
out.write(110);
|
||||
break;
|
||||
case '\u000b':
|
||||
default:
|
||||
if (ch > 15) {
|
||||
out.write("\\u00" + hex(ch));
|
||||
} else {
|
||||
out.write("\\u000" + hex(ch));
|
||||
}
|
||||
break;
|
||||
case '\f':
|
||||
out.write(92);
|
||||
out.write(102);
|
||||
break;
|
||||
case '\r':
|
||||
out.write(92);
|
||||
out.write(114);
|
||||
}
|
||||
} else {
|
||||
switch(ch) {
|
||||
case '"':
|
||||
if(!skipStrings) {
|
||||
out.write(92);
|
||||
}
|
||||
out.write(34);
|
||||
break;
|
||||
case '\'':
|
||||
if(!skipStrings) {
|
||||
out.write(92);
|
||||
}
|
||||
out.write(39);
|
||||
break;
|
||||
case '/':
|
||||
out.write(92);
|
||||
out.write(47);
|
||||
break;
|
||||
case '\\':
|
||||
out.write(92);
|
||||
out.write(92);
|
||||
break;
|
||||
default:
|
||||
out.write(ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
private static @NotNull String hex(char ch) {
|
||||
return Integer.toHexString(ch).toUpperCase();
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user