Skip to content

Commit

Permalink
Support for dynamic translation of item labels.
Browse files Browse the repository at this point in the history
Signed-off-by: Łukasz Dywicki <luke@code-house.org>
  • Loading branch information
splatch committed Oct 20, 2021
1 parent 0f0311f commit 9e39f63
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@
import java.time.DateTimeException;
import java.time.ZoneId;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.ResourceBundle;

import java.util.concurrent.CopyOnWriteArrayList;
import javax.measure.Quantity;
import javax.measure.Unit;
import javax.measure.quantity.Angle;
Expand All @@ -40,6 +42,7 @@
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.LocationProvider;
import org.openhab.core.i18n.ResourceTranslationProvider;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.i18n.TranslationProvider;
import org.openhab.core.i18n.UnitProvider;
Expand All @@ -57,6 +60,9 @@
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Modified;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.component.annotations.ReferencePolicy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -78,6 +84,7 @@
* @author Markus Rathgeb - Initial contribution
* @author Stefan Triller - Initial contribution
* @author Erdoan Hadzhiyusein - Added time zone
* @author Łukasz Dywicki - Added support for dynamic translations
*/
@Component(immediate = true, configurationPid = I18nProviderImpl.CONFIGURATION_PID, property = {
Constants.SERVICE_PID + "=org.openhab.i18n", //
Expand All @@ -101,6 +108,7 @@ public class I18nProviderImpl

// TranslationProvider
private final ResourceBundleTracker resourceBundleTracker;
private final List<ResourceTranslationProvider> translationProviders = new CopyOnWriteArrayList<>();

// LocationProvider
static final String LOCATION = "location";
Expand Down Expand Up @@ -146,6 +154,15 @@ protected synchronized void modified(Map<String, Object> config) {
setMeasurementSystem(measurementSystem);
}

@Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC)
void addResourceTranslationProvider(ResourceTranslationProvider translationProvider) {
this.translationProviders.add(translationProvider);
}

void removeResourceTranslationProvider(ResourceTranslationProvider translationProvider) {
this.translationProviders.remove(translationProvider);
}

private void setMeasurementSystem(@Nullable String measurementSystem) {
SystemOfUnits oldMeasurementSystem = this.measurementSystem;

Expand Down Expand Up @@ -299,6 +316,13 @@ public Locale getLocale() {
@Override
public @Nullable String getText(@Nullable Bundle bundle, @Nullable String key, @Nullable String defaultText,
@Nullable Locale locale) {
for (ResourceTranslationProvider provider : translationProviders) {
String text = provider.getText(key, locale);
if (text != null) {
return text;
}
}

LanguageResourceBundleManager languageResource = this.resourceBundleTracker.getLanguageResource(bundle);

if (languageResource != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.util.ResourceBundle.Control;

import org.openhab.core.common.osgi.ResourceBundleClassLoader;
import org.openhab.core.i18n.ResourceTranslationProvider;
import org.openhab.core.i18n.LocaleProvider;
import org.osgi.framework.Bundle;

Expand All @@ -37,7 +38,7 @@
* @author Michael Grammling - Initial contribution
* @author Markus Rathgeb - Add locale provider support
*/
public class LanguageResourceBundleManager {
public class LanguageResourceBundleManager implements ResourceTranslationProvider {

/** The directory within the bundle where the resource files are searched. */
protected static final String RESOURCE_DIRECTORY = "/OH-INF/i18n";
Expand Down Expand Up @@ -135,6 +136,7 @@ private List<String> determineResourceNames() {
*
* @return the translated text, or null if the key could not be translated
*/
@Override
public String getText(String resource, String key, Locale locale) {
if ((key != null) && (!key.isEmpty())) {
Locale effectiveLocale = locale != null ? locale : localeProvider.getLocale();
Expand Down Expand Up @@ -167,6 +169,7 @@ public String getText(String resource, String key, Locale locale) {
*
* @return the translated text, or null if the key could not be translated
*/
@Override
public String getText(String key, Locale locale) {
return getText(null, key, locale);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.core.i18n;

import java.util.Locale;

/**
* Interface for resource/translation provider which can be registered dynamically as a service.
*
* @author Łukasz Dywicki - Initial contribution.
*/
public interface ResourceTranslationProvider {

/**
* Returns a translation for the specified key in the specified locale (language) by only
* considering the specified resource section. The resource is equal to a base name and
* therefore it is mapped to one translation package (all files which belong to the base
* name).
* <p>
* If no translation could be found, {@code null} is returned. If the location is not specified, the default
* location is used.
*
* @param resource the resource to be used for look-up (could be null or empty)
* @param key the key to be translated (could be null or empty)
* @param locale the locale (language) to be used (could be null)
*
* @return the translated text, or null if the key could not be translated
*/
String getText(String resource, String key, Locale locale);

/**
* Returns a translation for the specified key in the specified locale (language)
* by considering all resources in the according bundle.
* <p>
* If no translation could be found, {@code null} is returned. If the location is not specified, the default
* location is used.
*
* @param key the key to be translated (could be null or empty)
* @param locale the locale (language) to be used (could be null)
*
* @return the translated text, or null if the key could not be translated
*/
String getText(String key, Locale locale);

}
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
import org.openhab.core.auth.Permissions;
import org.openhab.core.auth.Role;
import org.openhab.core.events.EventPublisher;
import org.openhab.core.i18n.TranslationProvider;
import org.openhab.core.io.rest.DTOMapper;
import org.openhab.core.io.rest.JSONResponse;
import org.openhab.core.io.rest.LocaleService;
Expand Down Expand Up @@ -173,6 +174,7 @@ private static void respectForwarded(final UriBuilder uriBuilder, final @Context
private final ItemBuilderFactory itemBuilderFactory;
private final ItemRegistry itemRegistry;
private final LocaleService localeService;
private final TranslationProvider translationProvider;
private final ManagedItemProvider managedItemProvider;
private final MetadataRegistry metadataRegistry;
private final MetadataSelectorMatcher metadataSelectorMatcher;
Expand All @@ -186,6 +188,7 @@ public ItemResource(//
final @Reference ItemBuilderFactory itemBuilderFactory, //
final @Reference ItemRegistry itemRegistry, //
final @Reference LocaleService localeService, //
final @Reference TranslationProvider translationProvider, //
final @Reference ManagedItemProvider managedItemProvider,
final @Reference MetadataRegistry metadataRegistry,
final @Reference MetadataSelectorMatcher metadataSelectorMatcher,
Expand All @@ -196,6 +199,7 @@ public ItemResource(//
this.itemBuilderFactory = itemBuilderFactory;
this.itemRegistry = itemRegistry;
this.localeService = localeService;
this.translationProvider = translationProvider;
this.managedItemProvider = managedItemProvider;
this.metadataRegistry = metadataRegistry;
this.metadataSelectorMatcher = metadataSelectorMatcher;
Expand Down Expand Up @@ -232,7 +236,8 @@ public Response getItems(final @Context UriInfo uriInfo, final @Context HttpHead
.filter(item -> authorizationManager.hasPermission(Permissions.READ, item, authentication))
.map(item -> EnrichedItemDTOMapper.map(item, recursive, null, uriBuilder, locale)) //
.peek(dto -> addMetadata(dto, namespaces, null)) //
.peek(dto -> dto.editable = isEditable(dto.name));
.peek(dto -> dto.editable = isEditable(dto.name))
.peek(dto -> dto.label = translationProvider.getText(null, "item." + dto.name, dto.label, locale));
itemStream = dtoMapper.limitToFields(itemStream, fields);
return Response.ok(new Stream2JSONInputStream(itemStream)).build();
}
Expand Down Expand Up @@ -264,6 +269,7 @@ public Response getItemData(final @Context UriInfo uriInfo, final @Context HttpH
EnrichedItemDTO dto = EnrichedItemDTOMapper.map(item, true, null, uriBuilder(uriInfo, httpHeaders), locale);
addMetadata(dto, namespaces, null);
dto.editable = isEditable(dto.name);
dto.label = translationProvider.getText(null, "item." + dto.name, dto.label, locale);
return JSONResponse.createResponse(Status.OK, dto, null);
} else {
return getItemNotFoundResponse(itemname);
Expand Down

0 comments on commit 9e39f63

Please sign in to comment.