Fix i18n:generate-default-translations on add-on config other than bindings (#4650)

* Fix i18n:generate-default-translations on add-on config other than bindings
* translate all xml config descriptions

Signed-off-by: Jimmy Tanagra <jcode@tanagra.id.au>
This commit is contained in:
jimtng
2025-04-02 20:03:37 +02:00
committed by GitHub
parent 6e87152b90
commit a670ddf2ef
3 changed files with 87 additions and 52 deletions
@@ -108,7 +108,7 @@ public class GenerateDefaultTranslationsMojo extends AbstractI18nMojo {
} }
protected String generateDefaultTranslations(Path defaultTranslationsPath) { protected String generateDefaultTranslations(Path defaultTranslationsPath) {
XmlToTranslationsConverter xmlConverter = new XmlToTranslationsConverter(); XmlToTranslationsConverter xmlConverter = new XmlToTranslationsConverter(getLog());
Translations generatedTranslations = xmlConverter.convert(bundleInfo); Translations generatedTranslations = xmlConverter.convert(bundleInfo);
JsonToTranslationsConverter jsonConverter = new JsonToTranslationsConverter(); JsonToTranslationsConverter jsonConverter = new JsonToTranslationsConverter();
@@ -16,6 +16,8 @@ import static org.openhab.core.tools.i18n.plugin.Translations.TranslationsEntry.
import static org.openhab.core.tools.i18n.plugin.Translations.TranslationsGroup.group; import static org.openhab.core.tools.i18n.plugin.Translations.TranslationsGroup.group;
import static org.openhab.core.tools.i18n.plugin.Translations.TranslationsSection.section; import static org.openhab.core.tools.i18n.plugin.Translations.TranslationsSection.section;
import java.net.URI;
import java.util.ArrayList;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@@ -24,7 +26,9 @@ import java.util.regex.Pattern;
import java.util.stream.Stream; import java.util.stream.Stream;
import java.util.stream.Stream.Builder; import java.util.stream.Stream.Builder;
import org.apache.maven.plugin.logging.Log;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.addon.AddonInfo; import org.openhab.core.addon.AddonInfo;
import org.openhab.core.addon.internal.xml.AddonInfoXmlResult; import org.openhab.core.addon.internal.xml.AddonInfoXmlResult;
import org.openhab.core.config.core.ConfigDescription; import org.openhab.core.config.core.ConfigDescription;
@@ -52,6 +56,12 @@ import org.openhab.core.types.StateDescription;
public class XmlToTranslationsConverter { public class XmlToTranslationsConverter {
private static final Pattern OPTION_ESCAPE_PATTERN = Pattern.compile("([ :=])"); private static final Pattern OPTION_ESCAPE_PATTERN = Pattern.compile("([ :=])");
private final Log log;
private List<String> processedURIs = new ArrayList<>();
public XmlToTranslationsConverter(Log log) {
this.log = log;
}
public Translations convert(BundleInfo bundleInfo) { public Translations convert(BundleInfo bundleInfo) {
String addonId = bundleInfo.getAddonId(); String addonId = bundleInfo.getAddonId();
@@ -59,30 +69,58 @@ public class XmlToTranslationsConverter {
} }
private Translations addonTranslations(BundleInfo bundleInfo) { private Translations addonTranslations(BundleInfo bundleInfo) {
return Translations.translations( // Builder<TranslationsSection> translationsBuilder = Stream.builder();
addonSection(bundleInfo), // translationsBuilder.add(addonSection(bundleInfo));
addonConfigSection(bundleInfo), // translationsBuilder.add(addonConfigSection(bundleInfo));
thingTypesSection(bundleInfo), // translationsBuilder.add(thingTypesSection(bundleInfo));
thingTypesConfigSection(bundleInfo), // translationsBuilder.add(thingTypesConfigSection(bundleInfo));
channelGroupTypesSection(bundleInfo), // translationsBuilder.add(channelGroupTypesSection(bundleInfo));
channelTypesSection(bundleInfo), // translationsBuilder.add(channelTypesSection(bundleInfo));
channelTypesConfigSection(bundleInfo)); translationsBuilder.add(channelTypesConfigSection(bundleInfo));
// Process the rest of config descriptions
Builder<TranslationsGroup> groupBuilder = Stream.builder();
bundleInfo.getConfigDescriptions().stream().map(this::translateConfigDescription).reduce(Stream::concat)
.orElseGet(Stream::empty).forEach(groupBuilder::add);
List<TranslationsGroup> groups = groupBuilder.build().toList();
if (!groups.isEmpty()) {
translationsBuilder.add(section(groups.stream()));
}
return Translations.translations(translationsBuilder.build());
}
private Stream<TranslationsGroup> translateConfigDescription(ConfigDescription configDescription) {
return translateConfigDescription(configDescription, null);
}
private Stream<TranslationsGroup> translateConfigDescription(ConfigDescription configDescription,
@Nullable String configKeyPrefix) {
String UID = configDescription.getUID().toString();
if (processedURIs.contains(UID)) {
return Stream.empty();
}
processedURIs.add(UID);
String keyPrefix = configKeyPrefix != null ? configKeyPrefix
: UID.replaceFirst(":", ".config.").replace(":", ".");
Builder<TranslationsGroup> streamBuilder = Stream.builder();
configDescriptionGroupParameters(keyPrefix, configDescription.getParameterGroups()).forEach(streamBuilder::add);
configDescriptionParameters(keyPrefix, configDescription.getParameters()).forEach(streamBuilder::add);
return streamBuilder.build();
}
private TranslationsSection configDescriptionsSection(BundleInfo bundleInfo) {
Builder<TranslationsGroup> groupsBuilder = Stream.builder();
bundleInfo.getConfigDescriptions().stream().map(this::translateConfigDescription).reduce(Stream::concat)
.orElseGet(Stream::empty).forEach(groupsBuilder::add);
return section(groupsBuilder.build());
} }
private Translations configTranslations(BundleInfo bundleInfo) { private Translations configTranslations(BundleInfo bundleInfo) {
Builder<TranslationsGroup> groupsBuilder = Stream.builder(); return Translations.translations(configDescriptionsSection(bundleInfo));
bundleInfo.getConfigDescriptions().stream().map(configDescription -> {
Object[] uid = configDescription.getUID().toString().split(":");
String configKeyPrefix = String.format("%s.config" + ".%s".repeat(uid.length - 1), uid);
Builder<TranslationsGroup> streamBuilder = Stream.builder();
configDescriptionGroupParameters(configKeyPrefix, configDescription.getParameterGroups())
.forEach(streamBuilder::add);
configDescriptionParameters(configKeyPrefix, configDescription.getParameters()).forEach(streamBuilder::add);
return streamBuilder.build();
}).reduce(Stream::concat).orElseGet(Stream::empty).forEach(groupsBuilder::add);
return Translations.translations(section(groupsBuilder.build()));
} }
private TranslationsSection addonSection(BundleInfo bundleInfo) { private TranslationsSection addonSection(BundleInfo bundleInfo) {
@@ -109,15 +147,28 @@ public class XmlToTranslationsConverter {
if (addonInfoXml == null) { if (addonInfoXml == null) {
return section(header); return section(header);
} }
ConfigDescription addonConfig = addonInfoXml.configDescription();
if (addonConfig == null) { Builder<TranslationsGroup> groupsBuilder = Stream.builder();
return section(header);
String configDescriptionURI = addonInfoXml.addonInfo().getConfigDescriptionURI();
if (configDescriptionURI != null) {
try {
Optional<ConfigDescription> configDescription = bundleInfo
.getConfigDescription(new URI(configDescriptionURI));
configDescription.map(this::translateConfigDescription).orElseGet(Stream::empty)
.forEach(groupsBuilder::add);
} catch (java.net.URISyntaxException e) {
log.warn("Invalid URI for config description: " + configDescriptionURI);
}
} }
String keyPrefix = String.format("addon.config.%s", bundleInfo.getAddonId()); ConfigDescription addonConfig = addonInfoXml.configDescription();
return section(header, if (addonConfig != null) {
Stream.concat(configDescriptionGroupParameters(keyPrefix, addonConfig.getParameterGroups()), String keyPrefix = String.format("addon.config.%s", bundleInfo.getAddonId());
configDescriptionParameters(keyPrefix, addonConfig.getParameters()))); translateConfigDescription(addonConfig, keyPrefix).forEach(groupsBuilder::add);
}
return section(header, groupsBuilder.build());
} }
private TranslationsSection thingTypesSection(BundleInfo bundleInfo) { private TranslationsSection thingTypesSection(BundleInfo bundleInfo) {
@@ -195,15 +246,8 @@ public class XmlToTranslationsConverter {
Builder<TranslationsGroup> groupsBuilder = Stream.builder(); Builder<TranslationsGroup> groupsBuilder = Stream.builder();
configDescriptionStream.map(configDescription -> { configDescriptionStream.map(this::translateConfigDescription).reduce(Stream::concat).orElseGet(Stream::empty)
String configKeyPrefix = String.format("%s.config.%s.%s", .forEach(groupsBuilder::add);
(Object[]) configDescription.getUID().toString().split(":"));
Builder<TranslationsGroup> streamBuilder = Stream.builder();
configDescriptionGroupParameters(configKeyPrefix, configDescription.getParameterGroups())
.forEach(streamBuilder::add);
configDescriptionParameters(configKeyPrefix, configDescription.getParameters()).forEach(streamBuilder::add);
return streamBuilder.build();
}).reduce(Stream::concat).orElseGet(Stream::empty).forEach(groupsBuilder::add);
return section(header, groupsBuilder.build()); return section(header, groupsBuilder.build());
} }
@@ -317,17 +361,8 @@ public class XmlToTranslationsConverter {
Builder<TranslationsGroup> groupsBuilder = Stream.builder(); Builder<TranslationsGroup> groupsBuilder = Stream.builder();
configDescriptionStream.map(configDescription -> { configDescriptionStream.map(this::translateConfigDescription).reduce(Stream::concat).orElseGet(Stream::empty)
String configKeyPrefix = String.format("%s.config.%s.%s", .forEach(groupsBuilder::add);
(Object[]) configDescription.getUID().toString().split(":"));
Builder<TranslationsGroup> streamBuilder = Stream.builder();
configDescriptionGroupParameters(configKeyPrefix, configDescription.getParameterGroups())
.forEach(streamBuilder::add);
configDescriptionParameters(configKeyPrefix, configDescription.getParameters()).forEach(streamBuilder::add);
return streamBuilder.build();
}).reduce(Stream::concat).orElseGet(Stream::empty).forEach(groupsBuilder::add);
return section(header, groupsBuilder.build()); return section(header, groupsBuilder.build());
} }
@@ -37,7 +37,7 @@ public class XmlToTranslationsConverterTest {
BundleInfoReader reader = new BundleInfoReader(new SystemStreamLog()); BundleInfoReader reader = new BundleInfoReader(new SystemStreamLog());
BundleInfo bundleInfo = reader.readBundleInfo(Path.of("src/test/resources/acmeweather.bundle/OH-INF")); BundleInfo bundleInfo = reader.readBundleInfo(Path.of("src/test/resources/acmeweather.bundle/OH-INF"));
XmlToTranslationsConverter converter = new XmlToTranslationsConverter(); XmlToTranslationsConverter converter = new XmlToTranslationsConverter(new SystemStreamLog());
Translations translations = converter.convert(bundleInfo); Translations translations = converter.convert(bundleInfo);
assertThat(translations.hasTranslations(), is(true)); assertThat(translations.hasTranslations(), is(true));
@@ -65,7 +65,7 @@ public class XmlToTranslationsConverterTest {
BundleInfoReader reader = new BundleInfoReader(new SystemStreamLog()); BundleInfoReader reader = new BundleInfoReader(new SystemStreamLog());
BundleInfo bundleInfo = reader.readBundleInfo(Path.of("src/test/resources/acmetts.bundle/OH-INF")); BundleInfo bundleInfo = reader.readBundleInfo(Path.of("src/test/resources/acmetts.bundle/OH-INF"));
XmlToTranslationsConverter converter = new XmlToTranslationsConverter(); XmlToTranslationsConverter converter = new XmlToTranslationsConverter(new SystemStreamLog());
Translations translations = converter.convert(bundleInfo); Translations translations = converter.convert(bundleInfo);
assertThat(translations.hasTranslations(), is(true)); assertThat(translations.hasTranslations(), is(true));
@@ -86,7 +86,7 @@ public class XmlToTranslationsConverterTest {
BundleInfoReader reader = new BundleInfoReader(new SystemStreamLog()); BundleInfoReader reader = new BundleInfoReader(new SystemStreamLog());
BundleInfo bundleInfo = reader.readBundleInfo(Path.of("src/test/resources/infoless.bundle/OH-INF")); BundleInfo bundleInfo = reader.readBundleInfo(Path.of("src/test/resources/infoless.bundle/OH-INF"));
XmlToTranslationsConverter converter = new XmlToTranslationsConverter(); XmlToTranslationsConverter converter = new XmlToTranslationsConverter(new SystemStreamLog());
Translations translations = converter.convert(bundleInfo); Translations translations = converter.convert(bundleInfo);
assertThat(translations.hasTranslations(), is(false)); assertThat(translations.hasTranslations(), is(false));