Improve escaping in i18n-maven-plugin (#2968)
* Also escape special characters in state option values And escaped space, = and : symbols (all legal according to properties documentation). Signed-off-by: Hilbrand Bouwkamp <hilbrand@h72.nl> * Properly escape : and \ in option Signed-off-by: Hilbrand Bouwkamp <hilbrand@h72.nl>pull/2973/head
parent
b52d9f2e4f
commit
3da9570fe7
|
@ -98,7 +98,7 @@ public class PropertiesToTranslationsConverter {
|
|||
int index = line.indexOf("=");
|
||||
if (index == -1) {
|
||||
log.warn("Ignoring invalid translation key/value pair: " + line);
|
||||
} else {
|
||||
} else if (!(index > 0 && line.charAt(index - 1) == '\\')) { // ignore escaped =
|
||||
if (entriesBuilder == null) {
|
||||
entriesBuilder = Stream.builder();
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ import java.util.Comparator;
|
|||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.Stream.Builder;
|
||||
|
||||
|
@ -51,6 +51,8 @@ import org.openhab.core.types.StateDescription;
|
|||
@NonNullByDefault
|
||||
public class XmlToTranslationsConverter {
|
||||
|
||||
private static final Pattern OPTION_ESCAPE_PATTERN = Pattern.compile("([ :=])");
|
||||
|
||||
public Translations convert(BundleInfo bundleInfo) {
|
||||
String bindingId = bundleInfo.getBindingId();
|
||||
return bindingId.isBlank() ? configTranslations(bundleInfo) : bindingTranslations(bundleInfo);
|
||||
|
@ -271,7 +273,8 @@ public class XmlToTranslationsConverter {
|
|||
if (stateDescription != null) {
|
||||
stateDescription.getOptions().stream()
|
||||
.map(option -> entry(
|
||||
String.format("%s.%s.state.option.%s", keyPrefix, channelId, option.getValue()),
|
||||
String.format("%s.%s.state.option.%s", keyPrefix, channelId,
|
||||
OPTION_ESCAPE_PATTERN.matcher(option.getValue()).replaceAll("\\\\$1")),
|
||||
option.getLabel()))
|
||||
.forEach(entriesBuilder::add);
|
||||
|
||||
|
@ -354,7 +357,7 @@ public class XmlToTranslationsConverter {
|
|||
parameter.getOptions().stream()
|
||||
.map(option -> entry(
|
||||
String.format("%s.option.%s", parameterKeyPrefix,
|
||||
option.getValue().replaceAll(" ", Matcher.quoteReplacement("\\ "))),
|
||||
OPTION_ESCAPE_PATTERN.matcher(option.getValue()).replaceAll("\\\\$1")),
|
||||
option.getLabel()))
|
||||
.forEach(entriesBuilder::add);
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ public class PropertiesToTranslationsConverterTest {
|
|||
|
||||
assertThat(translations.hasTranslations(), is(true));
|
||||
assertThat(translations.sections.size(), is(6));
|
||||
assertThat(translations.keysStream().count(), is(32L));
|
||||
assertThat(translations.keysStream().count(), is(34L));
|
||||
|
||||
String lines = translations.linesStream().collect(Collectors.joining(System.lineSeparator()));
|
||||
assertThat(lines, containsString("# binding"));
|
||||
|
|
|
@ -42,7 +42,7 @@ public class XmlToTranslationsConverterTest {
|
|||
|
||||
assertThat(translations.hasTranslations(), is(true));
|
||||
assertThat(translations.sections.size(), is(7));
|
||||
assertThat(translations.keysStream().count(), is(31L));
|
||||
assertThat(translations.keysStream().count(), is(34L));
|
||||
|
||||
String lines = translations.linesStream().collect(Collectors.joining(System.lineSeparator()));
|
||||
assertThat(lines, containsString("# binding"));
|
||||
|
@ -55,6 +55,9 @@ public class XmlToTranslationsConverterTest {
|
|||
"channel-type.acmeweather.time-stamp.state.pattern = %1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS"));
|
||||
assertThat(lines,
|
||||
containsString("thing-type.config.acmeweather.weather.language.option.de\\ DE = German (Germany)"));
|
||||
assertThat(lines, containsString("channel-type.acmeweather.temperature.state.option.VALUE\\ 1 = My label 1"));
|
||||
assertThat(lines, containsString("channel-type.acmeweather.temperature.state.option.VALUE\\:2 = My label 2"));
|
||||
assertThat(lines, containsString("channel-type.acmeweather.temperature.state.option.VALUE\\=3 = My label 3"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -40,6 +40,9 @@ channel-group-type.acmeweather.forecast.channel.minTemperature.description = Min
|
|||
channel-type.acmeweather.temperature.label = Temperature
|
||||
channel-type.acmeweather.temperature.description = Temperature in degrees Celsius (metric) or Fahrenheit (imperial).
|
||||
channel-type.acmeweather.temperature.state.option.VALUE = My label
|
||||
channel-type.acmeweather.temperature.state.option.VALUE\ 1 = My label 1
|
||||
channel-type.acmeweather.temperature.state.option.VALUE\:2 = My label 2
|
||||
channel-type.acmeweather.temperature.state.option.VALUE\=3 = My label 3
|
||||
channel-type.acmeweather.time-stamp.label = Observation Time
|
||||
channel-type.acmeweather.time-stamp.description = Time of data observation.
|
||||
channel-type.acmeweather.time-stamp.state.pattern = %1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS
|
||||
|
|
|
@ -40,6 +40,9 @@ channel-group-type.acmeweather.forecast.channel.minTemperature.description = Min
|
|||
channel-type.acmeweather.temperature.label = Temperature
|
||||
channel-type.acmeweather.temperature.description = Temperature in degrees Celsius (metric) or Fahrenheit (imperial).
|
||||
channel-type.acmeweather.temperature.state.option.VALUE = My label
|
||||
channel-type.acmeweather.temperature.state.option.VALUE\ 1 = My label 1
|
||||
channel-type.acmeweather.temperature.state.option.VALUE\:2 = My label 2
|
||||
channel-type.acmeweather.temperature.state.option.VALUE\=3 = My label 3
|
||||
channel-type.acmeweather.time-stamp.label = Observation Time
|
||||
channel-type.acmeweather.time-stamp.description = Time of data observation.
|
||||
channel-type.acmeweather.time-stamp.state.pattern = %1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS
|
||||
|
|
|
@ -40,6 +40,9 @@ channel-group-type.acmeweather.forecast.channel.minTemperature.description = Min
|
|||
channel-type.acmeweather.temperature.label = Temperatur
|
||||
channel-type.acmeweather.temperature.description = Temperatur in Grad Celsius (Metrisch) oder Fahrenheit (Imperial).
|
||||
channel-type.acmeweather.temperature.state.option.VALUE = Mein String
|
||||
channel-type.acmeweather.temperature.state.option.VALUE\ 1 = Mein String 1
|
||||
channel-type.acmeweather.temperature.state.option.VALUE\:2 = Mein String 2
|
||||
channel-type.acmeweather.temperature.state.option.VALUE\=3 = Mein String 3
|
||||
channel-type.acmeweather.time-stamp.label = Letzte Messung
|
||||
channel-type.acmeweather.time-stamp.description = Zeigt den Zeitpunkt der letzten Messung an.
|
||||
channel-type.acmeweather.time-stamp.state.pattern = %1$td.%1$tm.%1$tY %1$tH:%1$tM:%1$tS
|
||||
|
|
|
@ -29,6 +29,9 @@
|
|||
<state pattern="%d degree Celsius">
|
||||
<options>
|
||||
<option value="VALUE">My label</option>
|
||||
<option value="VALUE 1">My label 1</option>
|
||||
<option value="VALUE:2">My label 2</option>
|
||||
<option value="VALUE=3">My label 3</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
|
Loading…
Reference in New Issue