[knx] Improve localization (#13293)
* [knx] Improve localization - introduce localization of error messages - add new strings for common exceptions - provide helper functions for translation - add test cases Signed-off-by: Holger Friedrich <mail@holger-friedrich.de>
This commit is contained in:
parent
baa8d208ea
commit
4e69e17f41
@ -28,6 +28,7 @@ import org.eclipse.jdt.annotation.Nullable;
|
|||||||
import org.openhab.binding.knx.internal.KNXTypeMapper;
|
import org.openhab.binding.knx.internal.KNXTypeMapper;
|
||||||
import org.openhab.binding.knx.internal.dpt.KNXCoreTypeMapper;
|
import org.openhab.binding.knx.internal.dpt.KNXCoreTypeMapper;
|
||||||
import org.openhab.binding.knx.internal.handler.GroupAddressListener;
|
import org.openhab.binding.knx.internal.handler.GroupAddressListener;
|
||||||
|
import org.openhab.binding.knx.internal.i18n.KNXTranslationProvider;
|
||||||
import org.openhab.core.thing.ThingStatus;
|
import org.openhab.core.thing.ThingStatus;
|
||||||
import org.openhab.core.thing.ThingStatusDetail;
|
import org.openhab.core.thing.ThingStatusDetail;
|
||||||
import org.openhab.core.thing.ThingUID;
|
import org.openhab.core.thing.ThingUID;
|
||||||
@ -289,9 +290,8 @@ public abstract class AbstractKNXClient implements NetworkLinkListener, KNXClien
|
|||||||
private synchronized void disconnect(@Nullable Exception e, Optional<ThingStatusDetail> detail) {
|
private synchronized void disconnect(@Nullable Exception e, Optional<ThingStatusDetail> detail) {
|
||||||
releaseConnection();
|
releaseConnection();
|
||||||
if (e != null) {
|
if (e != null) {
|
||||||
final String message = e.getLocalizedMessage();
|
|
||||||
statusUpdateCallback.updateStatus(ThingStatus.OFFLINE, detail.orElse(ThingStatusDetail.COMMUNICATION_ERROR),
|
statusUpdateCallback.updateStatus(ThingStatus.OFFLINE, detail.orElse(ThingStatusDetail.COMMUNICATION_ERROR),
|
||||||
message != null ? message : "");
|
KNXTranslationProvider.I18N.getLocalizedException(e));
|
||||||
} else {
|
} else {
|
||||||
statusUpdateCallback.updateStatus(ThingStatus.OFFLINE);
|
statusUpdateCallback.updateStatus(ThingStatus.OFFLINE);
|
||||||
}
|
}
|
||||||
@ -406,8 +406,9 @@ public abstract class AbstractKNXClient implements NetworkLinkListener, KNXClien
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!link.isOpen() && CloseEvent.USER_REQUEST != closeEvent.getInitiator()) {
|
if (!link.isOpen() && CloseEvent.USER_REQUEST != closeEvent.getInitiator()) {
|
||||||
|
final String reason = closeEvent.getReason();
|
||||||
statusUpdateCallback.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
statusUpdateCallback.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||||
closeEvent.getReason());
|
KNXTranslationProvider.I18N.get(reason));
|
||||||
logger.debug("KNX link has been lost (reason: {} on object {})", closeEvent.getReason(),
|
logger.debug("KNX link has been lost (reason: {} on object {})", closeEvent.getReason(),
|
||||||
closeEvent.getSource().toString());
|
closeEvent.getSource().toString());
|
||||||
scheduleReconnectJob();
|
scheduleReconnectJob();
|
||||||
|
|||||||
@ -20,7 +20,10 @@ import java.util.Collection;
|
|||||||
import org.openhab.binding.knx.internal.handler.DeviceThingHandler;
|
import org.openhab.binding.knx.internal.handler.DeviceThingHandler;
|
||||||
import org.openhab.binding.knx.internal.handler.IPBridgeThingHandler;
|
import org.openhab.binding.knx.internal.handler.IPBridgeThingHandler;
|
||||||
import org.openhab.binding.knx.internal.handler.SerialBridgeThingHandler;
|
import org.openhab.binding.knx.internal.handler.SerialBridgeThingHandler;
|
||||||
|
import org.openhab.binding.knx.internal.i18n.KNXTranslationProvider;
|
||||||
import org.openhab.core.config.core.Configuration;
|
import org.openhab.core.config.core.Configuration;
|
||||||
|
import org.openhab.core.i18n.LocaleProvider;
|
||||||
|
import org.openhab.core.i18n.TranslationProvider;
|
||||||
import org.openhab.core.net.NetworkAddressService;
|
import org.openhab.core.net.NetworkAddressService;
|
||||||
import org.openhab.core.thing.Bridge;
|
import org.openhab.core.thing.Bridge;
|
||||||
import org.openhab.core.thing.Thing;
|
import org.openhab.core.thing.Thing;
|
||||||
@ -29,6 +32,7 @@ import org.openhab.core.thing.ThingUID;
|
|||||||
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||||
import org.openhab.core.thing.binding.ThingHandler;
|
import org.openhab.core.thing.binding.ThingHandler;
|
||||||
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||||
|
import org.osgi.service.component.annotations.Activate;
|
||||||
import org.osgi.service.component.annotations.Component;
|
import org.osgi.service.component.annotations.Component;
|
||||||
import org.osgi.service.component.annotations.Reference;
|
import org.osgi.service.component.annotations.Reference;
|
||||||
|
|
||||||
@ -46,6 +50,12 @@ public class KNXHandlerFactory extends BaseThingHandlerFactory {
|
|||||||
|
|
||||||
private NetworkAddressService networkAddressService;
|
private NetworkAddressService networkAddressService;
|
||||||
|
|
||||||
|
@Activate
|
||||||
|
public KNXHandlerFactory(final @Reference TranslationProvider translationProvider,
|
||||||
|
final @Reference LocaleProvider localeProvider) {
|
||||||
|
KNXTranslationProvider.I18N.setProvider(localeProvider, translationProvider);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||||
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
||||||
|
|||||||
@ -25,6 +25,7 @@ import org.openhab.binding.knx.internal.client.DeviceInspector;
|
|||||||
import org.openhab.binding.knx.internal.client.DeviceInspector.Result;
|
import org.openhab.binding.knx.internal.client.DeviceInspector.Result;
|
||||||
import org.openhab.binding.knx.internal.client.KNXClient;
|
import org.openhab.binding.knx.internal.client.KNXClient;
|
||||||
import org.openhab.binding.knx.internal.config.DeviceConfig;
|
import org.openhab.binding.knx.internal.config.DeviceConfig;
|
||||||
|
import org.openhab.binding.knx.internal.i18n.KNXTranslationProvider;
|
||||||
import org.openhab.core.thing.Bridge;
|
import org.openhab.core.thing.Bridge;
|
||||||
import org.openhab.core.thing.Thing;
|
import org.openhab.core.thing.Thing;
|
||||||
import org.openhab.core.thing.ThingStatus;
|
import org.openhab.core.thing.ThingStatus;
|
||||||
@ -164,7 +165,8 @@ public abstract class AbstractKNXThingHandler extends BaseThingHandler implement
|
|||||||
} catch (KNXException e) {
|
} catch (KNXException e) {
|
||||||
logger.debug("An error occurred while testing the reachability of a thing '{}': {}", getThing().getUID(),
|
logger.debug("An error occurred while testing the reachability of a thing '{}': {}", getThing().getUID(),
|
||||||
e.getMessage());
|
e.getMessage());
|
||||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getLocalizedMessage());
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||||
|
KNXTranslationProvider.I18N.getLocalizedException(e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,7 +196,8 @@ public abstract class AbstractKNXThingHandler extends BaseThingHandler implement
|
|||||||
} catch (KNXFormatException e) {
|
} catch (KNXFormatException e) {
|
||||||
logger.debug("An exception occurred while setting the individual address '{}': {}", config.getAddress(),
|
logger.debug("An exception occurred while setting the individual address '{}': {}", config.getAddress(),
|
||||||
e.getMessage());
|
e.getMessage());
|
||||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getLocalizedMessage());
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||||
|
KNXTranslationProvider.I18N.getLocalizedException(e));
|
||||||
}
|
}
|
||||||
getClient().registerGroupAddressListener(this);
|
getClient().registerGroupAddressListener(this);
|
||||||
scheduleReadJobs();
|
scheduleReadJobs();
|
||||||
|
|||||||
@ -23,6 +23,7 @@ import org.openhab.binding.knx.internal.client.IPClient;
|
|||||||
import org.openhab.binding.knx.internal.client.KNXClient;
|
import org.openhab.binding.knx.internal.client.KNXClient;
|
||||||
import org.openhab.binding.knx.internal.client.NoOpClient;
|
import org.openhab.binding.knx.internal.client.NoOpClient;
|
||||||
import org.openhab.binding.knx.internal.config.IPBridgeConfiguration;
|
import org.openhab.binding.knx.internal.config.IPBridgeConfiguration;
|
||||||
|
import org.openhab.binding.knx.internal.i18n.KNXTranslationProvider;
|
||||||
import org.openhab.core.net.NetworkAddressService;
|
import org.openhab.core.net.NetworkAddressService;
|
||||||
import org.openhab.core.thing.Bridge;
|
import org.openhab.core.thing.Bridge;
|
||||||
import org.openhab.core.thing.ThingStatus;
|
import org.openhab.core.thing.ThingStatus;
|
||||||
@ -87,11 +88,12 @@ public class IPBridgeThingHandler extends KNXBridgeBaseThingHandler {
|
|||||||
} catch (KnxSecureException e) {
|
} catch (KnxSecureException e) {
|
||||||
logger.debug("{}, {}", thing.getUID(), e.toString());
|
logger.debug("{}, {}", thing.getUID(), e.toString());
|
||||||
|
|
||||||
String message = e.getLocalizedMessage();
|
Throwable cause = e.getCause();
|
||||||
if (message == null) {
|
if (cause == null) {
|
||||||
message = e.getClass().getSimpleName();
|
cause = e;
|
||||||
}
|
}
|
||||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "KNX security: " + message);
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||||
|
KNXTranslationProvider.I18N.getLocalizedException(cause));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,7 +122,7 @@ public class IPBridgeThingHandler extends KNXBridgeBaseThingHandler {
|
|||||||
if (!securityAvailable) {
|
if (!securityAvailable) {
|
||||||
logger.warn("Bridge {} missing security configuration for secure tunnel", thing.getUID());
|
logger.warn("Bridge {} missing security configuration for secure tunnel", thing.getUID());
|
||||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||||
"Security configuration missing for secure tunnel");
|
"@text/error.knx-secure-tunnel-config-missing");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
boolean tunnelOk = ((secureTunnel.user > 0) && (secureTunnel.devKey.length == 16)
|
boolean tunnelOk = ((secureTunnel.user > 0) && (secureTunnel.devKey.length == 16)
|
||||||
@ -128,7 +130,7 @@ public class IPBridgeThingHandler extends KNXBridgeBaseThingHandler {
|
|||||||
if (!tunnelOk) {
|
if (!tunnelOk) {
|
||||||
logger.warn("Bridge {} incomplete security configuration for secure tunnel", thing.getUID());
|
logger.warn("Bridge {} incomplete security configuration for secure tunnel", thing.getUID());
|
||||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||||
"Security configuration for secure tunnel is incomplete");
|
"@text/error.knx-secure-tunnel-config-incomplete");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,22 +152,21 @@ public class IPBridgeThingHandler extends KNXBridgeBaseThingHandler {
|
|||||||
if (!securityAvailable) {
|
if (!securityAvailable) {
|
||||||
logger.warn("Bridge {} missing security configuration for secure routing", thing.getUID());
|
logger.warn("Bridge {} missing security configuration for secure routing", thing.getUID());
|
||||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||||
"Security configuration missing for secure routing");
|
"@text/error.knx-secure-routing-config-missing");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (secureRouting.backboneGroupKey.length != 16) {
|
if (secureRouting.backboneGroupKey.length != 16) {
|
||||||
// failed to read shared backbone group key from config
|
// failed to read shared backbone group key from config
|
||||||
logger.warn("Bridge {} missing security configuration for secure routing", thing.getUID());
|
logger.warn("Bridge {} invalid security configuration for secure routing", thing.getUID());
|
||||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||||
"backboneGroupKey required for secure routing; please configure");
|
"@text/error.knx-secure-routing-backbonegroupkey-invalid");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
logger.debug("KNX secure routing needs a few seconds to establish connection");
|
logger.debug("KNX secure routing needs a few seconds to establish connection");
|
||||||
} else {
|
} else {
|
||||||
logger.debug("Bridge {} unknown connection type", thing.getUID());
|
logger.debug("Bridge {} unknown connection type", thing.getUID());
|
||||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, MessageFormat.format(
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||||
"Unknown IP connection type {0}. Known types are either 'TUNNEL', 'ROUTER', 'SECURETUNNEL', or 'SECUREROUTER'",
|
MessageFormat.format("@text/knx-unknown-ip-connection-type", connectionTypeString));
|
||||||
connectionTypeString));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,128 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2022 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.binding.knx.internal.i18n;
|
||||||
|
|
||||||
|
import java.text.MessageFormat;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.core.i18n.LocaleProvider;
|
||||||
|
import org.openhab.core.i18n.TranslationProvider;
|
||||||
|
import org.osgi.framework.Bundle;
|
||||||
|
import org.osgi.framework.FrameworkUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class provides translations. It is a helper class for i18n / localization efforts.
|
||||||
|
*
|
||||||
|
* @implNote It is implemented as a static singleton, enforced by the single-element enum pattern.
|
||||||
|
* @apiNote @set() must be called to provide tanslation service, otherwise all functions will return untranslated text.
|
||||||
|
* Thread safety is ensured.
|
||||||
|
* @author Holger Friedrich - Initial contribution
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public enum KNXTranslationProvider {
|
||||||
|
I18N;
|
||||||
|
|
||||||
|
private @Nullable LocaleProvider localeProvider;
|
||||||
|
private @Nullable TranslationProvider translationProvider;
|
||||||
|
private Bundle bundle;
|
||||||
|
|
||||||
|
private KNXTranslationProvider() {
|
||||||
|
localeProvider = null;
|
||||||
|
translationProvider = null;
|
||||||
|
bundle = FrameworkUtil.getBundle(this.getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get translated text
|
||||||
|
*
|
||||||
|
* @param text text to be translated, may contain placeholders \{n\} for the n-th optional argument of this function
|
||||||
|
* @param arguments any optional arguments, will be inserted
|
||||||
|
* @return translated text with subsitutions if translationprovide is set and provides a translation, otherwise
|
||||||
|
* returns original text with substitutions
|
||||||
|
*/
|
||||||
|
public String get(final String text, @Nullable Object @Nullable... arguments) {
|
||||||
|
// ensure thread safety: calls to set(..) should not lead to race condition
|
||||||
|
final TranslationProvider translationProvider = this.translationProvider;
|
||||||
|
final LocaleProvider localeProvider = this.localeProvider;
|
||||||
|
if (translationProvider != null) {
|
||||||
|
// localeProvider might be null, but if not, getLocale will return NonNull Locale
|
||||||
|
// locale cannot be cached, as getLocale() will return different result once locale is changed by user
|
||||||
|
final Locale locale = (localeProvider != null) ? localeProvider.getLocale() : Locale.getDefault();
|
||||||
|
final String res = translationProvider.getText(bundle, text, text, locale, arguments);
|
||||||
|
if (res != null) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// translating not possible, we still have the original text without any subsititutions
|
||||||
|
if (arguments == null || arguments.length == 0) {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
// else execute pattern substitution in untranslated text
|
||||||
|
return MessageFormat.format(text, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get exception in user readable (and possibly localized) form
|
||||||
|
*
|
||||||
|
* @param e any exception
|
||||||
|
* @return localized message in form <description (translated)> (<class name>, <e.getLocalizedMessage (not
|
||||||
|
* translated)>), empty string for null. May possibly change in further releases.
|
||||||
|
*/
|
||||||
|
public String getLocalizedException(final Throwable e) {
|
||||||
|
StringBuffer res = new StringBuffer();
|
||||||
|
final String exName = e.getClass().getSimpleName();
|
||||||
|
final String key = "exception." + exName;
|
||||||
|
final String translatedDescription = KNXTranslationProvider.I18N.get(key);
|
||||||
|
Boolean foundTranslation = !key.equals(translatedDescription);
|
||||||
|
// detailed message cannot be translated, e.getLocalizedMessage will likely return English
|
||||||
|
String detail = e.getLocalizedMessage();
|
||||||
|
if (detail == null) {
|
||||||
|
detail = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundTranslation) {
|
||||||
|
res.append(translatedDescription);
|
||||||
|
res.append(" (");
|
||||||
|
res.append(exName);
|
||||||
|
if (!detail.isBlank()) {
|
||||||
|
res.append(", ");
|
||||||
|
res.append(detail);
|
||||||
|
}
|
||||||
|
res.append(")");
|
||||||
|
} else {
|
||||||
|
res.append(exName);
|
||||||
|
if (!detail.isBlank()) {
|
||||||
|
res.append(", ");
|
||||||
|
res.append(detail);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set translation providers. To be called to make any translation work.
|
||||||
|
*
|
||||||
|
* @param localeProvider openHAB locale provider, can be generated via \@Activate / \@Reference LocaleProvider in
|
||||||
|
* handler factory
|
||||||
|
* @param translationProvider openHAB locale provider, can be generated via \@Activate / \@Reference
|
||||||
|
* TranslationProvider in handler factory
|
||||||
|
*/
|
||||||
|
public void setProvider(@Nullable LocaleProvider localeProvider,
|
||||||
|
@Nullable TranslationProvider translationProvider) {
|
||||||
|
this.localeProvider = localeProvider;
|
||||||
|
this.translationProvider = translationProvider;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -151,3 +151,29 @@ channel-type.config.knx.single.ga.description = The group address(es) in Group A
|
|||||||
|
|
||||||
thing-type.config.knx.serial.group.knxsecure.label = KNX secure
|
thing-type.config.knx.serial.group.knxsecure.label = KNX secure
|
||||||
thing-type.config.knx.serial.group.knxsecure.description = Settings for KNX secure. Requires KNX secure features to be active in KNX installation.
|
thing-type.config.knx.serial.group.knxsecure.description = Settings for KNX secure. Requires KNX secure features to be active in KNX installation.
|
||||||
|
|
||||||
|
# binding specific strings
|
||||||
|
|
||||||
|
error.knx-secure-routing-backbonegroupkey-invalid = backbonegroupkey invalid, please check if it is specified correctly
|
||||||
|
error.knx-secure-routing-config-missing = Security configuration missing for secure routing
|
||||||
|
error.knx-secure-tunnel-config-incomplete = Security configuration for secure tunnel is incomplete
|
||||||
|
error.knx-secure-tunnel-config-missing = Security configuration for secure tunnel is missing
|
||||||
|
error.knx-unknown-ip-connection-type = Unknown IP connection type: {0}. Known types are either 'TUNNEL', 'ROUTER', 'SECURETUNNEL', or 'SECUREROUTER'.
|
||||||
|
|
||||||
|
# user readable description of exceptions
|
||||||
|
|
||||||
|
exception.KNXAckTimeoutException = Communication timeout, missing response
|
||||||
|
exception.KNXConnectionClosedException = Connection closed
|
||||||
|
exception.KNXDisconnectException = Disconnected
|
||||||
|
exception.KNXException = KNX exception
|
||||||
|
exception.KNXFormatException = Data format not valid
|
||||||
|
exception.KNXIllegalArgumentException = Illegal argument
|
||||||
|
exception.KNXInvalidResponseException = Invalid response from KNX device
|
||||||
|
exception.KNXLinkClosedException = Link closed, cannot communicate
|
||||||
|
exception.KNXMLException = Error processing XML data
|
||||||
|
exception.KNXPortClosedException = Port closed, cannot communicate
|
||||||
|
exception.KnxPropertyException = Error accessing KNX property
|
||||||
|
exception.KNXRemoteException = KNX device indicates error
|
||||||
|
exception.KnxRuntimeException = Runtime error
|
||||||
|
exception.KnxSecureException = Error processing KNX secure data
|
||||||
|
exception.KNXTimeoutException = Communication timeout
|
||||||
|
|||||||
@ -0,0 +1,111 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2022 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.binding.knx.internal.i18n;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import tuwien.auto.calimero.KNXException;
|
||||||
|
import tuwien.auto.calimero.link.KNXLinkClosedException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Holger Friedrich - initial contribution
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class KNXTranslationProviderTest {
|
||||||
|
static final String UNKNOWN = "unknown text";
|
||||||
|
static final String UNKNOWN_PATTERN = "unknown text {0}";
|
||||||
|
static final String UNKNOWN_FIVE = "unknown text 5";
|
||||||
|
static final String UNKNOWN_NULL = "unknown text null";
|
||||||
|
static final String KNX_BINDING_KEY = "binding.knx.name";
|
||||||
|
static final String KNX_BINDING_VALUE = "KNX Binding";
|
||||||
|
static final String CONN_TYPE_PATTERN_KEY = "error.knx-unknown-ip-connection-type";
|
||||||
|
static final String CONN_TYPE_PATTERN_VALUE = "Unknown IP connection type: {0}.";
|
||||||
|
static final String CONN_TYPE_FIVE_VALUE = "Unknown IP connection type: 5.";
|
||||||
|
static final String CONN_TYPE_NULL_VALUE = "Unknown IP connection type: null.";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetBeforeInit() {
|
||||||
|
// initial state, should not crash and preferrably return original strings (w. pattern substitution)
|
||||||
|
assertEquals(UNKNOWN, KNXTranslationProvider.I18N.get(UNKNOWN));
|
||||||
|
assertEquals(UNKNOWN, KNXTranslationProvider.I18N.get(UNKNOWN, 5));
|
||||||
|
assertEquals(UNKNOWN_NULL, KNXTranslationProvider.I18N.get(UNKNOWN_PATTERN, null, null));
|
||||||
|
assertEquals(UNKNOWN_FIVE, KNXTranslationProvider.I18N.get(UNKNOWN_PATTERN, 5));
|
||||||
|
// KNXTranslationProvider.I18N.get(..., null) would cause a compiler warning,
|
||||||
|
// but using a null object of a defined type, it is possible to invoke with null value
|
||||||
|
String s = null;
|
||||||
|
assertEquals(UNKNOWN, KNXTranslationProvider.I18N.get(UNKNOWN, s));
|
||||||
|
assertEquals(UNKNOWN_NULL, KNXTranslationProvider.I18N.get(UNKNOWN_PATTERN, s));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetProvider() {
|
||||||
|
// initial state, should not crash
|
||||||
|
KNXTranslationProvider.I18N.setProvider(null, null);
|
||||||
|
assertNotNull(KNXTranslationProvider.I18N.get(UNKNOWN));
|
||||||
|
|
||||||
|
// use mockup classes with known dictionary
|
||||||
|
KNXTranslationProvider.I18N.setProvider(new MockedLocaleProvider(), new MockedTranslationProvider());
|
||||||
|
assertEquals(KNX_BINDING_VALUE, KNXTranslationProvider.I18N.get(KNX_BINDING_KEY));
|
||||||
|
assertEquals(CONN_TYPE_FIVE_VALUE, KNXTranslationProvider.I18N.get(CONN_TYPE_PATTERN_KEY, 5));
|
||||||
|
assertEquals(UNKNOWN, KNXTranslationProvider.I18N.get(UNKNOWN));
|
||||||
|
assertEquals(UNKNOWN, KNXTranslationProvider.I18N.get(UNKNOWN, 5));
|
||||||
|
assertEquals(UNKNOWN_NULL, KNXTranslationProvider.I18N.get(UNKNOWN_PATTERN, null, null));
|
||||||
|
assertEquals(UNKNOWN_FIVE, KNXTranslationProvider.I18N.get(UNKNOWN_PATTERN, 5));
|
||||||
|
// KNXTranslationProvider.I18N.get(..., null) would cause a compiler warning,
|
||||||
|
// but using a null object of a defined type, it is possible to invoke with null value
|
||||||
|
String s = null;
|
||||||
|
assertEquals(UNKNOWN, KNXTranslationProvider.I18N.get(UNKNOWN, s));
|
||||||
|
assertEquals(UNKNOWN_NULL, KNXTranslationProvider.I18N.get(UNKNOWN_PATTERN, s));
|
||||||
|
|
||||||
|
assertEquals(CONN_TYPE_NULL_VALUE, KNXTranslationProvider.I18N.get(CONN_TYPE_PATTERN_KEY, s));
|
||||||
|
assertEquals(CONN_TYPE_PATTERN_VALUE, KNXTranslationProvider.I18N.get(CONN_TYPE_PATTERN_KEY));
|
||||||
|
|
||||||
|
// no locale, should work as fallback to default locale
|
||||||
|
KNXTranslationProvider.I18N.setProvider(null, new MockedTranslationProvider());
|
||||||
|
assertEquals(KNXTranslationProvider.I18N.get(KNX_BINDING_KEY), KNX_BINDING_VALUE);
|
||||||
|
|
||||||
|
// no translations, should return initial string
|
||||||
|
KNXTranslationProvider.I18N.setProvider(new MockedLocaleProvider(), null);
|
||||||
|
assertEquals(KNX_BINDING_KEY, KNXTranslationProvider.I18N.get(KNX_BINDING_KEY));
|
||||||
|
|
||||||
|
// initial state, dictionary should be gone
|
||||||
|
KNXTranslationProvider.I18N.setProvider(null, null);
|
||||||
|
assertEquals(KNX_BINDING_KEY, KNXTranslationProvider.I18N.get(KNX_BINDING_KEY));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetLocalizedException() {
|
||||||
|
// initial state, should not crash
|
||||||
|
KNXTranslationProvider.I18N.setProvider(null, null);
|
||||||
|
|
||||||
|
final Exception e = new KNXException("error 1");
|
||||||
|
final Exception se = new KNXLinkClosedException("connection closed", e);
|
||||||
|
assertNotNull(KNXTranslationProvider.I18N.getLocalizedException(e));
|
||||||
|
assertNotNull(KNXTranslationProvider.I18N.getLocalizedException(se));
|
||||||
|
assertEquals("KNXException, error 1", KNXTranslationProvider.I18N.getLocalizedException(e));
|
||||||
|
|
||||||
|
// use mockup classes with known dictionary
|
||||||
|
KNXTranslationProvider.I18N.setProvider(new MockedLocaleProvider(), new MockedTranslationProvider());
|
||||||
|
// exception which is not part off the dictionary
|
||||||
|
assertEquals("KNXLinkClosedException, connection closed",
|
||||||
|
KNXTranslationProvider.I18N.getLocalizedException(se));
|
||||||
|
// exception which can be translated
|
||||||
|
assertEquals("Translated KNX Exception (KNXException, error 1)",
|
||||||
|
KNXTranslationProvider.I18N.getLocalizedException(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2022 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.binding.knx.internal.i18n;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.openhab.core.i18n.LocaleProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locale provider for unit tests.
|
||||||
|
*
|
||||||
|
* @author Jacob Laursen - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class MockedLocaleProvider implements LocaleProvider {
|
||||||
|
public Locale getLocale() {
|
||||||
|
return Locale.ENGLISH;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2022 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.binding.knx.internal.i18n;
|
||||||
|
|
||||||
|
import static java.util.Map.entry;
|
||||||
|
|
||||||
|
import java.text.MessageFormat;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.core.i18n.TranslationProvider;
|
||||||
|
import org.osgi.framework.Bundle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translation provider for unit tests.
|
||||||
|
*
|
||||||
|
* @author Jacob Laursen - Initial contribution
|
||||||
|
* @author Holger Friedrich - Imported from hdpowerview, adapted
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class MockedTranslationProvider implements TranslationProvider {
|
||||||
|
|
||||||
|
private static final Map<String, String> TEXTS = Map.ofEntries(entry("binding.knx.name", "KNX Binding"),
|
||||||
|
entry("error.knx-unknown-ip-connection-type", "Unknown IP connection type: {0}."),
|
||||||
|
entry("exception.KNXException", "Translated KNX Exception"));
|
||||||
|
|
||||||
|
public MockedTranslationProvider() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public String getText(@Nullable Bundle bundle, @Nullable String key, @Nullable String defaultText,
|
||||||
|
@Nullable Locale locale) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public String getText(@Nullable Bundle bundle, @Nullable String key, @Nullable String defaultText,
|
||||||
|
@Nullable Locale locale, @Nullable Object @Nullable... arguments) {
|
||||||
|
String text = TEXTS.get(key);
|
||||||
|
return MessageFormat.format(text != null ? text : key, arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user