thingsboard-aplcache

Changes

common/pom.xml 2(+1 -1)

dao/pom.xml 2(+1 -1)

pom.xml 2(+1 -1)

tools/pom.xml 2(+1 -1)

ui/package.json 2(+1 -1)

ui/pom.xml 2(+1 -1)

Details

diff --git a/application/pom.xml b/application/pom.xml
index 8fa2091..3ce9260 100644
--- a/application/pom.xml
+++ b/application/pom.xml
@@ -20,7 +20,7 @@
     <modelVersion>4.0.0</modelVersion>
     <parent>
         <groupId>org.thingsboard</groupId>
-        <version>2.0.1-SNAPSHOT</version>
+        <version>2.0.2-SNAPSHOT</version>
         <artifactId>thingsboard</artifactId>
     </parent>
     <artifactId>application</artifactId>
diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
index 85b3285..9527171 100644
--- a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
@@ -294,7 +294,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
     private void handleGetAttributesRequest(DeviceToDeviceActorMsg src) {
         GetAttributesRequest request = (GetAttributesRequest) src.getPayload();
         ListenableFuture<List<AttributeKvEntry>> clientAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.CLIENT_SCOPE, request.getClientAttributeNames());
-        ListenableFuture<List<AttributeKvEntry>> sharedAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.SHARED_SCOPE, request.getClientAttributeNames());
+        ListenableFuture<List<AttributeKvEntry>> sharedAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.SHARED_SCOPE, request.getSharedAttributeNames());
 
         Futures.addCallback(Futures.allAsList(Arrays.asList(clientAttributesFuture, sharedAttributesFuture)), new FutureCallback<List<List<AttributeKvEntry>>>() {
             @Override
diff --git a/application/src/test/java/org/thingsboard/server/system/BaseHttpDeviceApiTest.java b/application/src/test/java/org/thingsboard/server/system/BaseHttpDeviceApiTest.java
index 4fa6162..5e6896b 100644
--- a/application/src/test/java/org/thingsboard/server/system/BaseHttpDeviceApiTest.java
+++ b/application/src/test/java/org/thingsboard/server/system/BaseHttpDeviceApiTest.java
@@ -64,6 +64,7 @@ public abstract class BaseHttpDeviceApiTest extends AbstractControllerTest {
         mockMvc.perform(
                 asyncDispatch(doPost("/api/v1/" + deviceCredentials.getCredentialsId() + "/attributes", attrMap, new String[]{}).andReturn()))
                 .andExpect(status().isOk());
+        Thread.sleep(2000);
         doGetAsync("/api/v1/" + deviceCredentials.getCredentialsId() + "/attributes?clientKeys=keyA,keyB,keyC").andExpect(status().isOk());
     }
 
diff --git a/common/data/pom.xml b/common/data/pom.xml
index 666aad4..425edc2 100644
--- a/common/data/pom.xml
+++ b/common/data/pom.xml
@@ -20,7 +20,7 @@
     <modelVersion>4.0.0</modelVersion>
     <parent>
         <groupId>org.thingsboard</groupId>
-        <version>2.0.1-SNAPSHOT</version>
+        <version>2.0.2-SNAPSHOT</version>
         <artifactId>common</artifactId>
     </parent>
     <groupId>org.thingsboard.common</groupId>
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java
index ff908fc..7b74e62 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java
@@ -98,6 +98,7 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa
     public boolean updateAssignedCustomer(Customer customer) {
         ShortCustomerInfo customerInfo = customer.toShortCustomerInfo();
         if (this.assignedCustomers != null && this.assignedCustomers.contains(customerInfo)) {
+            this.assignedCustomers.remove(customerInfo);
             this.assignedCustomers.add(customerInfo);
             return true;
         } else {
diff --git a/common/message/pom.xml b/common/message/pom.xml
index cb33fd6..33a9320 100644
--- a/common/message/pom.xml
+++ b/common/message/pom.xml
@@ -20,7 +20,7 @@
     <modelVersion>4.0.0</modelVersion>
     <parent>
         <groupId>org.thingsboard</groupId>
-        <version>2.0.1-SNAPSHOT</version>
+        <version>2.0.2-SNAPSHOT</version>
         <artifactId>common</artifactId>
     </parent>
     <groupId>org.thingsboard.common</groupId>

common/pom.xml 2(+1 -1)

diff --git a/common/pom.xml b/common/pom.xml
index 4123c26..b96ab67 100644
--- a/common/pom.xml
+++ b/common/pom.xml
@@ -20,7 +20,7 @@
     <modelVersion>4.0.0</modelVersion>
     <parent>
         <groupId>org.thingsboard</groupId>
-        <version>2.0.1-SNAPSHOT</version>
+        <version>2.0.2-SNAPSHOT</version>
         <artifactId>thingsboard</artifactId>
     </parent>
     <groupId>org.thingsboard</groupId>
diff --git a/common/transport/pom.xml b/common/transport/pom.xml
index dfce87e..c62123a 100644
--- a/common/transport/pom.xml
+++ b/common/transport/pom.xml
@@ -20,7 +20,7 @@
     <modelVersion>4.0.0</modelVersion>
     <parent>
         <groupId>org.thingsboard</groupId>
-        <version>2.0.1-SNAPSHOT</version>
+        <version>2.0.2-SNAPSHOT</version>
         <artifactId>common</artifactId>
     </parent>
     <groupId>org.thingsboard.common</groupId>

dao/pom.xml 2(+1 -1)

diff --git a/dao/pom.xml b/dao/pom.xml
index 5e3ca5a..8bf5693 100644
--- a/dao/pom.xml
+++ b/dao/pom.xml
@@ -20,7 +20,7 @@
     <modelVersion>4.0.0</modelVersion>
     <parent>
         <groupId>org.thingsboard</groupId>
-        <version>2.0.1-SNAPSHOT</version>
+        <version>2.0.2-SNAPSHOT</version>
         <artifactId>thingsboard</artifactId>
     </parent>
     <artifactId>dao</artifactId>
diff --git a/netty-mqtt/pom.xml b/netty-mqtt/pom.xml
index 657e936..da4fcd0 100644
--- a/netty-mqtt/pom.xml
+++ b/netty-mqtt/pom.xml
@@ -19,12 +19,12 @@
     <modelVersion>4.0.0</modelVersion>
     <parent>
         <groupId>org.thingsboard</groupId>
-        <version>2.0.1-SNAPSHOT</version>
+        <version>2.0.2-SNAPSHOT</version>
         <artifactId>thingsboard</artifactId>
     </parent>
     <groupId>org.thingsboard</groupId>
     <artifactId>netty-mqtt</artifactId>
-    <version>2.0.1-SNAPSHOT</version>
+    <version>2.0.2-SNAPSHOT</version>
     <packaging>jar</packaging>
 
     <name>Netty MQTT Client</name>
diff --git a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttChannelHandler.java b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttChannelHandler.java
index ef5e7a5..c927c42 100644
--- a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttChannelHandler.java
+++ b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttChannelHandler.java
@@ -98,33 +98,25 @@ final class MqttChannelHandler extends SimpleChannelInboundHandler<MqttMessage> 
     }
 
     private void invokeHandlersForIncomingPublish(MqttPublishMessage message) {
-        for (MqttSubscribtion subscribtion : ImmutableSet.copyOf(this.client.getSubscriptions().values())) {
-            if (subscribtion.matches(message.variableHeader().topicName())) {
-                if (subscribtion.isOnce() && subscribtion.isCalled()) {
+        boolean handlerInvoked = false;
+        for (MqttSubscription subscription : ImmutableSet.copyOf(this.client.getSubscriptions().values())) {
+            if (subscription.matches(message.variableHeader().topicName())) {
+                if (subscription.isOnce() && subscription.isCalled()) {
                     continue;
                 }
                 message.payload().markReaderIndex();
-                subscribtion.setCalled(true);
-                subscribtion.getHandler().onMessage(message.variableHeader().topicName(), message.payload());
-                if (subscribtion.isOnce()) {
-                    this.client.off(subscribtion.getTopic(), subscribtion.getHandler());
+                subscription.setCalled(true);
+                subscription.getHandler().onMessage(message.variableHeader().topicName(), message.payload());
+                if (subscription.isOnce()) {
+                    this.client.off(subscription.getTopic(), subscription.getHandler());
                 }
                 message.payload().resetReaderIndex();
+                handlerInvoked = true;
             }
         }
-        /*Set<MqttSubscribtion> subscribtions = ImmutableSet.copyOf(this.client.getSubscriptions().get(message.variableHeader().topicName()));
-        for (MqttSubscribtion subscribtion : subscribtions) {
-            if(subscribtion.isOnce() && subscribtion.isCalled()){
-                continue;
-            }
-            message.payload().markReaderIndex();
-            subscribtion.setCalled(true);
-            subscribtion.getHandler().onMessage(message.variableHeader().topicName(), message.payload());
-            if(subscribtion.isOnce()){
-                this.client.off(subscribtion.getTopic(), subscribtion.getHandler());
-            }
-            message.payload().resetReaderIndex();
-        }*/
+        if (!handlerInvoked && client.getDefaultHandler() != null) {
+            client.getDefaultHandler().onMessage(message.variableHeader().topicName(), message.payload());
+        }
         message.payload().release();
     }
 
@@ -133,7 +125,7 @@ final class MqttChannelHandler extends SimpleChannelInboundHandler<MqttMessage> 
             case CONNECTION_ACCEPTED:
                 this.connectFuture.setSuccess(new MqttConnectResult(true, MqttConnectReturnCode.CONNECTION_ACCEPTED, channel.closeFuture()));
 
-                this.client.getPendingSubscribtions().entrySet().stream().filter((e) -> !e.getValue().isSent()).forEach((e) -> {
+                this.client.getPendingSubscriptions().entrySet().stream().filter((e) -> !e.getValue().isSent()).forEach((e) -> {
                     channel.write(e.getValue().getSubscribeMessage());
                     e.getValue().setSent(true);
                 });
@@ -148,6 +140,9 @@ final class MqttChannelHandler extends SimpleChannelInboundHandler<MqttMessage> 
                     }
                 });
                 channel.flush();
+                if (this.client.isReconnect()) {
+                    this.client.onSuccessfulReconnect();
+                }
                 break;
 
             case CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD:
@@ -163,19 +158,19 @@ final class MqttChannelHandler extends SimpleChannelInboundHandler<MqttMessage> 
     }
 
     private void handleSubAck(MqttSubAckMessage message) {
-        MqttPendingSubscribtion pendingSubscription = this.client.getPendingSubscribtions().remove(message.variableHeader().messageId());
+        MqttPendingSubscription pendingSubscription = this.client.getPendingSubscriptions().remove(message.variableHeader().messageId());
         if (pendingSubscription == null) {
             return;
         }
         pendingSubscription.onSubackReceived();
-        for (MqttPendingSubscribtion.MqttPendingHandler handler : pendingSubscription.getHandlers()) {
-            MqttSubscribtion subscribtion = new MqttSubscribtion(pendingSubscription.getTopic(), handler.getHandler(), handler.isOnce());
-            this.client.getSubscriptions().put(pendingSubscription.getTopic(), subscribtion);
-            this.client.getHandlerToSubscribtion().put(handler.getHandler(), subscribtion);
+        for (MqttPendingSubscription.MqttPendingHandler handler : pendingSubscription.getHandlers()) {
+            MqttSubscription subscription = new MqttSubscription(pendingSubscription.getTopic(), handler.getHandler(), handler.isOnce());
+            this.client.getSubscriptions().put(pendingSubscription.getTopic(), subscription);
+            this.client.getHandlerToSubscribtion().put(handler.getHandler(), subscription);
         }
         this.client.getPendingSubscribeTopics().remove(pendingSubscription.getTopic());
 
-        this.client.getServerSubscribtions().add(pendingSubscription.getTopic());
+        this.client.getServerSubscriptions().add(pendingSubscription.getTopic());
 
         if (!pendingSubscription.getFuture().isDone()) {
             pendingSubscription.getFuture().setSuccess(null);
@@ -215,13 +210,13 @@ final class MqttChannelHandler extends SimpleChannelInboundHandler<MqttMessage> 
     }
 
     private void handleUnsuback(MqttUnsubAckMessage message) {
-        MqttPendingUnsubscribtion unsubscribtion = this.client.getPendingServerUnsubscribes().get(message.variableHeader().messageId());
-        if (unsubscribtion == null) {
+        MqttPendingUnsubscription unsubscription = this.client.getPendingServerUnsubscribes().get(message.variableHeader().messageId());
+        if (unsubscription == null) {
             return;
         }
-        unsubscribtion.onUnsubackReceived();
-        this.client.getServerSubscribtions().remove(unsubscribtion.getTopic());
-        unsubscribtion.getFuture().setSuccess(null);
+        unsubscription.onUnsubackReceived();
+        this.client.getServerSubscriptions().remove(unsubscription.getTopic());
+        unsubscription.getFuture().setSuccess(null);
         this.client.getPendingServerUnsubscribes().remove(message.variableHeader().messageId());
     }
 
diff --git a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClient.java b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClient.java
index 6563525..37efca3 100644
--- a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClient.java
+++ b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClient.java
@@ -92,7 +92,7 @@ public interface MqttClient {
 
     /**
      * Subscribe on the given topic. When a message is received, MqttClient will invoke the {@link MqttHandler#onMessage(String, ByteBuf)} function of the given handler
-     * This subscribtion is only once. If the MqttClient has received 1 message, the subscribtion will be removed
+     * This subscription is only once. If the MqttClient has received 1 message, the subscription will be removed
      *
      * @param topic The topic filter to subscribe to
      * @param handler The handler to invoke when we receive a message
@@ -102,7 +102,7 @@ public interface MqttClient {
 
     /**
      * Subscribe on the given topic, with the given qos. When a message is received, MqttClient will invoke the {@link MqttHandler#onMessage(String, ByteBuf)} function of the given handler
-     * This subscribtion is only once. If the MqttClient has received 1 message, the subscribtion will be removed
+     * This subscription is only once. If the MqttClient has received 1 message, the subscription will be removed
      *
      * @param topic The topic filter to subscribe to
      * @param handler The handler to invoke when we receive a message
@@ -112,7 +112,7 @@ public interface MqttClient {
     Future<Void> once(String topic, MqttHandler handler, MqttQoS qos);
 
     /**
-     * Remove the subscribtion for the given topic and handler
+     * Remove the subscription for the given topic and handler
      * If you want to unsubscribe from all handlers known for this topic, use {@link #off(String)}
      *
      * @param topic The topic to unsubscribe for
@@ -122,7 +122,7 @@ public interface MqttClient {
     Future<Void> off(String topic, MqttHandler handler);
 
     /**
-     * Remove all subscribtions for the given topic.
+     * Remove all subscriptions for the given topic.
      * If you want to specify which handler to unsubscribe, use {@link #off(String, MqttHandler)}
      *
      * @param topic The topic to unsubscribe for
@@ -172,24 +172,18 @@ public interface MqttClient {
      */
     MqttClientConfig getClientConfig();
 
-    /**
-     * Construct the MqttClientImpl with default config
-     */
-    static MqttClient create(){
-        return new MqttClientImpl();
-    }
 
     /**
      * Construct the MqttClientImpl with additional config.
      * This config can also be changed using the {@link #getClientConfig()} function
      *
      * @param config The config object to use while looking for settings
+     * @param defaultHandler The handler for incoming messages that do not match any topic subscriptions
      */
-    static MqttClient create(MqttClientConfig config){
-        return new MqttClientImpl(config);
+    static MqttClient create(MqttClientConfig config, MqttHandler defaultHandler){
+        return new MqttClientImpl(config, defaultHandler);
     }
 
-
     /**
      * Send disconnect and close channel
      *
diff --git a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientCallback.java b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientCallback.java
index d7f0a08..9f86b8e 100644
--- a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientCallback.java
+++ b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientCallback.java
@@ -15,6 +15,8 @@
  */
 package org.thingsboard.mqtt;
 
+import io.netty.channel.ChannelId;
+
 /**
  * Created by Valerii Sosliuk on 12/30/2017.
  */
@@ -25,5 +27,11 @@ public interface MqttClientCallback {
      *
      * @param cause the reason behind the loss of connection.
      */
-    public void connectionLost(Throwable cause);
+    void connectionLost(Throwable cause);
+
+    /**
+     * This method is called when the connection to the server is recovered.
+     *
+     */
+    void onSuccessfulReconnect();
 }
diff --git a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientImpl.java b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientImpl.java
index 3914105..a5df846 100644
--- a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientImpl.java
+++ b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientImpl.java
@@ -40,23 +40,26 @@ import java.util.concurrent.atomic.AtomicInteger;
 @SuppressWarnings({"WeakerAccess", "unused"})
 final class MqttClientImpl implements MqttClient {
 
-    private final Set<String> serverSubscribtions = new HashSet<>();
-    private final IntObjectHashMap<MqttPendingUnsubscribtion> pendingServerUnsubscribes = new IntObjectHashMap<>();
+    private final Set<String> serverSubscriptions = new HashSet<>();
+    private final IntObjectHashMap<MqttPendingUnsubscription> pendingServerUnsubscribes = new IntObjectHashMap<>();
     private final IntObjectHashMap<MqttIncomingQos2Publish> qos2PendingIncomingPublishes = new IntObjectHashMap<>();
     private final IntObjectHashMap<MqttPendingPublish> pendingPublishes = new IntObjectHashMap<>();
-    private final HashMultimap<String, MqttSubscribtion> subscriptions = HashMultimap.create();
-    private final IntObjectHashMap<MqttPendingSubscribtion> pendingSubscribtions = new IntObjectHashMap<>();
+    private final HashMultimap<String, MqttSubscription> subscriptions = HashMultimap.create();
+    private final IntObjectHashMap<MqttPendingSubscription> pendingSubscriptions = new IntObjectHashMap<>();
     private final Set<String> pendingSubscribeTopics = new HashSet<>();
-    private final HashMultimap<MqttHandler, MqttSubscribtion> handlerToSubscribtion = HashMultimap.create();
+    private final HashMultimap<MqttHandler, MqttSubscription> handlerToSubscribtion = HashMultimap.create();
     private final AtomicInteger nextMessageId = new AtomicInteger(1);
 
     private final MqttClientConfig clientConfig;
 
+    private final MqttHandler defaultHandler;
+
     private EventLoopGroup eventLoop;
 
-    private Channel channel;
+    private volatile Channel channel;
 
-    private boolean disconnected = false;
+    private volatile boolean disconnected = false;
+    private volatile boolean reconnect = false;
     private String host;
     private int port;
     private MqttClientCallback callback;
@@ -65,8 +68,9 @@ final class MqttClientImpl implements MqttClient {
     /**
      * Construct the MqttClientImpl with default config
      */
-    public MqttClientImpl() {
+    public MqttClientImpl(MqttHandler defaultHandler) {
         this.clientConfig = new MqttClientConfig();
+        this.defaultHandler = defaultHandler;
     }
 
     /**
@@ -75,8 +79,9 @@ final class MqttClientImpl implements MqttClient {
      *
      * @param clientConfig The config object to use while looking for settings
      */
-    public MqttClientImpl(MqttClientConfig clientConfig) {
+    public MqttClientImpl(MqttClientConfig clientConfig, MqttHandler defaultHandler) {
         this.clientConfig = clientConfig;
+        this.defaultHandler = defaultHandler;
     }
 
     /**
@@ -100,12 +105,15 @@ final class MqttClientImpl implements MqttClient {
      */
     @Override
     public Future<MqttConnectResult> connect(String host, int port) {
+        return connect(host, port, false);
+    }
+
+    private Future<MqttConnectResult> connect(String host, int port, boolean reconnect) {
         if (this.eventLoop == null) {
             this.eventLoop = new NioEventLoopGroup();
         }
         this.host = host;
         this.port = port;
-
         Promise<MqttConnectResult> connectFuture = new DefaultPromise<>(this.eventLoop.next());
         Bootstrap bootstrap = new Bootstrap();
         bootstrap.group(this.eventLoop);
@@ -113,22 +121,47 @@ final class MqttClientImpl implements MqttClient {
         bootstrap.remoteAddress(host, port);
         bootstrap.handler(new MqttChannelInitializer(connectFuture, host, port, clientConfig.getSslContext()));
         ChannelFuture future = bootstrap.connect();
+
         future.addListener((ChannelFutureListener) f -> {
             if (f.isSuccess()) {
                 MqttClientImpl.this.channel = f.channel();
-            } else if (clientConfig.isReconnect() && !disconnected) {
-                eventLoop.schedule((Runnable) () -> connect(host, port), 1L, TimeUnit.SECONDS);
+                MqttClientImpl.this.channel.closeFuture().addListener((ChannelFutureListener) channelFuture -> {
+                    if (isConnected()) {
+                        return;
+                    }
+                    ChannelClosedException e = new ChannelClosedException("Channel is closed!");
+                    if (callback != null) {
+                        callback.connectionLost(e);
+                    }
+                    pendingSubscriptions.clear();
+                    serverSubscriptions.clear();
+                    subscriptions.clear();
+                    pendingServerUnsubscribes.clear();
+                    qos2PendingIncomingPublishes.clear();
+                    pendingPublishes.clear();
+                    pendingSubscribeTopics.clear();
+                    handlerToSubscribtion.clear();
+                    scheduleConnectIfRequired(host, port, true);
+                });
+            } else {
+                scheduleConnectIfRequired(host, port, reconnect);
             }
         });
         return connectFuture;
     }
 
+    private void scheduleConnectIfRequired(String host, int port, boolean reconnect) {
+        if (clientConfig.isReconnect() && !disconnected) {
+            if (reconnect) {
+                this.reconnect = true;
+            }
+            eventLoop.schedule((Runnable) () -> connect(host, port, reconnect), 1L, TimeUnit.SECONDS);
+        }
+    }
+
     @Override
     public boolean isConnected() {
-        if (!disconnected) {
-            return channel == null ? false : channel.isActive();
-        };
-        return false;
+        return !disconnected && channel != null && channel.isActive();
     }
 
     @Override
@@ -183,12 +216,12 @@ final class MqttClientImpl implements MqttClient {
      */
     @Override
     public Future<Void> on(String topic, MqttHandler handler, MqttQoS qos) {
-        return createSubscribtion(topic, handler, false, qos);
+        return createSubscription(topic, handler, false, qos);
     }
 
     /**
      * Subscribe on the given topic. When a message is received, MqttClient will invoke the {@link MqttHandler#onMessage(String, ByteBuf)} function of the given handler
-     * This subscribtion is only once. If the MqttClient has received 1 message, the subscribtion will be removed
+     * This subscription is only once. If the MqttClient has received 1 message, the subscription will be removed
      *
      * @param topic   The topic filter to subscribe to
      * @param handler The handler to invoke when we receive a message
@@ -201,7 +234,7 @@ final class MqttClientImpl implements MqttClient {
 
     /**
      * Subscribe on the given topic, with the given qos. When a message is received, MqttClient will invoke the {@link MqttHandler#onMessage(String, ByteBuf)} function of the given handler
-     * This subscribtion is only once. If the MqttClient has received 1 message, the subscribtion will be removed
+     * This subscription is only once. If the MqttClient has received 1 message, the subscription will be removed
      *
      * @param topic   The topic filter to subscribe to
      * @param handler The handler to invoke when we receive a message
@@ -210,11 +243,11 @@ final class MqttClientImpl implements MqttClient {
      */
     @Override
     public Future<Void> once(String topic, MqttHandler handler, MqttQoS qos) {
-        return createSubscribtion(topic, handler, true, qos);
+        return createSubscription(topic, handler, true, qos);
     }
 
     /**
-     * Remove the subscribtion for the given topic and handler
+     * Remove the subscription for the given topic and handler
      * If you want to unsubscribe from all handlers known for this topic, use {@link #off(String)}
      *
      * @param topic   The topic to unsubscribe for
@@ -224,8 +257,8 @@ final class MqttClientImpl implements MqttClient {
     @Override
     public Future<Void> off(String topic, MqttHandler handler) {
         Promise<Void> future = new DefaultPromise<>(this.eventLoop.next());
-        for (MqttSubscribtion subscribtion : this.handlerToSubscribtion.get(handler)) {
-            this.subscriptions.remove(topic, subscribtion);
+        for (MqttSubscription subscription : this.handlerToSubscribtion.get(handler)) {
+            this.subscriptions.remove(topic, subscription);
         }
         this.handlerToSubscribtion.removeAll(handler);
         this.checkSubscribtions(topic, future);
@@ -233,7 +266,7 @@ final class MqttClientImpl implements MqttClient {
     }
 
     /**
-     * Remove all subscribtions for the given topic.
+     * Remove all subscriptions for the given topic.
      * If you want to specify which handler to unsubscribe, use {@link #off(String, MqttHandler)}
      *
      * @param topic The topic to unsubscribe for
@@ -242,12 +275,12 @@ final class MqttClientImpl implements MqttClient {
     @Override
     public Future<Void> off(String topic) {
         Promise<Void> future = new DefaultPromise<>(this.eventLoop.next());
-        ImmutableSet<MqttSubscribtion> subscribtions = ImmutableSet.copyOf(this.subscriptions.get(topic));
-        for (MqttSubscribtion subscribtion : subscribtions) {
-            for (MqttSubscribtion handSub : this.handlerToSubscribtion.get(subscribtion.getHandler())) {
+        ImmutableSet<MqttSubscription> subscriptions = ImmutableSet.copyOf(this.subscriptions.get(topic));
+        for (MqttSubscription subscription : subscriptions) {
+            for (MqttSubscription handSub : this.handlerToSubscribtion.get(subscription.getHandler())) {
                 this.subscriptions.remove(topic, handSub);
             }
-            this.handlerToSubscribtion.remove(subscribtion.getHandler(), subscribtion);
+            this.handlerToSubscribtion.remove(subscription.getHandler(), subscription);
         }
         this.checkSubscribtions(topic, future);
         return future;
@@ -310,7 +343,7 @@ final class MqttClientImpl implements MqttClient {
         ChannelFuture channelFuture = this.sendAndFlushPacket(message);
 
         if (channelFuture != null) {
-            pendingPublish.setSent(channelFuture != null);
+            pendingPublish.setSent(true);
             if (channelFuture.cause() != null) {
                 future.setFailure(channelFuture.cause());
                 return future;
@@ -352,6 +385,15 @@ final class MqttClientImpl implements MqttClient {
 
     ///////////////////////////////////////////// PRIVATE API /////////////////////////////////////////////
 
+    public boolean isReconnect() {
+        return reconnect;
+    }
+
+    public void onSuccessfulReconnect() {
+        callback.onSuccessfulReconnect();
+    }
+
+
     ChannelFuture sendAndFlushPacket(Object message) {
         if (this.channel == null) {
             return null;
@@ -359,11 +401,7 @@ final class MqttClientImpl implements MqttClient {
         if (this.channel.isActive()) {
             return this.channel.writeAndFlush(message);
         }
-        ChannelClosedException e = new ChannelClosedException("Channel is closed");
-        if (callback != null) {
-            callback.connectionLost(e);
-        }
-        return this.channel.newFailedFuture(e);
+        return this.channel.newFailedFuture(new ChannelClosedException("Channel is closed!"));
     }
 
     private MqttMessageIdVariableHeader getNewMessageId() {
@@ -371,18 +409,18 @@ final class MqttClientImpl implements MqttClient {
         return MqttMessageIdVariableHeader.from(this.nextMessageId.getAndIncrement());
     }
 
-    private Future<Void> createSubscribtion(String topic, MqttHandler handler, boolean once, MqttQoS qos) {
+    private Future<Void> createSubscription(String topic, MqttHandler handler, boolean once, MqttQoS qos) {
         if (this.pendingSubscribeTopics.contains(topic)) {
-            Optional<Map.Entry<Integer, MqttPendingSubscribtion>> subscribtionEntry = this.pendingSubscribtions.entrySet().stream().filter((e) -> e.getValue().getTopic().equals(topic)).findAny();
-            if (subscribtionEntry.isPresent()) {
-                subscribtionEntry.get().getValue().addHandler(handler, once);
-                return subscribtionEntry.get().getValue().getFuture();
+            Optional<Map.Entry<Integer, MqttPendingSubscription>> subscriptionEntry = this.pendingSubscriptions.entrySet().stream().filter((e) -> e.getValue().getTopic().equals(topic)).findAny();
+            if (subscriptionEntry.isPresent()) {
+                subscriptionEntry.get().getValue().addHandler(handler, once);
+                return subscriptionEntry.get().getValue().getFuture();
             }
         }
-        if (this.serverSubscribtions.contains(topic)) {
-            MqttSubscribtion subscribtion = new MqttSubscribtion(topic, handler, once);
-            this.subscriptions.put(topic, subscribtion);
-            this.handlerToSubscribtion.put(handler, subscribtion);
+        if (this.serverSubscriptions.contains(topic)) {
+            MqttSubscription subscription = new MqttSubscription(topic, handler, once);
+            this.subscriptions.put(topic, subscription);
+            this.handlerToSubscribtion.put(handler, subscription);
             return this.channel.newSucceededFuture();
         }
 
@@ -393,27 +431,27 @@ final class MqttClientImpl implements MqttClient {
         MqttSubscribePayload payload = new MqttSubscribePayload(Collections.singletonList(subscription));
         MqttSubscribeMessage message = new MqttSubscribeMessage(fixedHeader, variableHeader, payload);
 
-        final MqttPendingSubscribtion pendingSubscribtion = new MqttPendingSubscribtion(future, topic, message);
-        pendingSubscribtion.addHandler(handler, once);
-        this.pendingSubscribtions.put(variableHeader.messageId(), pendingSubscribtion);
+        final MqttPendingSubscription pendingSubscription = new MqttPendingSubscription(future, topic, message);
+        pendingSubscription.addHandler(handler, once);
+        this.pendingSubscriptions.put(variableHeader.messageId(), pendingSubscription);
         this.pendingSubscribeTopics.add(topic);
-        pendingSubscribtion.setSent(this.sendAndFlushPacket(message) != null); //If not sent, we will send it when the connection is opened
+        pendingSubscription.setSent(this.sendAndFlushPacket(message) != null); //If not sent, we will send it when the connection is opened
 
-        pendingSubscribtion.startRetransmitTimer(this.eventLoop.next(), this::sendAndFlushPacket);
+        pendingSubscription.startRetransmitTimer(this.eventLoop.next(), this::sendAndFlushPacket);
 
         return future;
     }
 
     private void checkSubscribtions(String topic, Promise<Void> promise) {
-        if (!(this.subscriptions.containsKey(topic) && this.subscriptions.get(topic).size() != 0) && this.serverSubscribtions.contains(topic)) {
+        if (!(this.subscriptions.containsKey(topic) && this.subscriptions.get(topic).size() != 0) && this.serverSubscriptions.contains(topic)) {
             MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.UNSUBSCRIBE, false, MqttQoS.AT_LEAST_ONCE, false, 0);
             MqttMessageIdVariableHeader variableHeader = getNewMessageId();
             MqttUnsubscribePayload payload = new MqttUnsubscribePayload(Collections.singletonList(topic));
             MqttUnsubscribeMessage message = new MqttUnsubscribeMessage(fixedHeader, variableHeader, payload);
 
-            MqttPendingUnsubscribtion pendingUnsubscribtion = new MqttPendingUnsubscribtion(promise, topic, message);
-            this.pendingServerUnsubscribes.put(variableHeader.messageId(), pendingUnsubscribtion);
-            pendingUnsubscribtion.startRetransmissionTimer(this.eventLoop.next(), this::sendAndFlushPacket);
+            MqttPendingUnsubscription pendingUnsubscription = new MqttPendingUnsubscription(promise, topic, message);
+            this.pendingServerUnsubscribes.put(variableHeader.messageId(), pendingUnsubscription);
+            pendingUnsubscription.startRetransmissionTimer(this.eventLoop.next(), this::sendAndFlushPacket);
 
             this.sendAndFlushPacket(message);
         } else {
@@ -421,11 +459,11 @@ final class MqttClientImpl implements MqttClient {
         }
     }
 
-    IntObjectHashMap<MqttPendingSubscribtion> getPendingSubscribtions() {
-        return pendingSubscribtions;
+    IntObjectHashMap<MqttPendingSubscription> getPendingSubscriptions() {
+        return pendingSubscriptions;
     }
 
-    HashMultimap<String, MqttSubscribtion> getSubscriptions() {
+    HashMultimap<String, MqttSubscription> getSubscriptions() {
         return subscriptions;
     }
 
@@ -433,15 +471,15 @@ final class MqttClientImpl implements MqttClient {
         return pendingSubscribeTopics;
     }
 
-    HashMultimap<MqttHandler, MqttSubscribtion> getHandlerToSubscribtion() {
+    HashMultimap<MqttHandler, MqttSubscription> getHandlerToSubscribtion() {
         return handlerToSubscribtion;
     }
 
-    Set<String> getServerSubscribtions() {
-        return serverSubscribtions;
+    Set<String> getServerSubscriptions() {
+        return serverSubscriptions;
     }
 
-    IntObjectHashMap<MqttPendingUnsubscribtion> getPendingServerUnsubscribes() {
+    IntObjectHashMap<MqttPendingUnsubscription> getPendingServerUnsubscribes() {
         return pendingServerUnsubscribes;
     }
 
@@ -481,4 +519,9 @@ final class MqttClientImpl implements MqttClient {
             ch.pipeline().addLast("mqttHandler", new MqttChannelHandler(MqttClientImpl.this, connectFuture));
         }
     }
+
+    MqttHandler getDefaultHandler() {
+        return defaultHandler;
+    }
+
 }

pom.xml 2(+1 -1)

diff --git a/pom.xml b/pom.xml
index 210e220..bd50609 100755
--- a/pom.xml
+++ b/pom.xml
@@ -20,7 +20,7 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>org.thingsboard</groupId>
     <artifactId>thingsboard</artifactId>
-    <version>2.0.1-SNAPSHOT</version>
+    <version>2.0.2-SNAPSHOT</version>
     <packaging>pom</packaging>
 
     <name>Thingsboard</name>
diff --git a/rule-engine/pom.xml b/rule-engine/pom.xml
index a3e74fe..f6ed888 100644
--- a/rule-engine/pom.xml
+++ b/rule-engine/pom.xml
@@ -20,7 +20,7 @@
     <modelVersion>4.0.0</modelVersion>
     <parent>
         <groupId>org.thingsboard</groupId>
-        <version>2.0.1-SNAPSHOT</version>
+        <version>2.0.2-SNAPSHOT</version>
         <artifactId>thingsboard</artifactId>
     </parent>
     <artifactId>rule-engine</artifactId>
diff --git a/rule-engine/rule-engine-api/pom.xml b/rule-engine/rule-engine-api/pom.xml
index a4ef070..3b0ee16 100644
--- a/rule-engine/rule-engine-api/pom.xml
+++ b/rule-engine/rule-engine-api/pom.xml
@@ -22,7 +22,7 @@
     <modelVersion>4.0.0</modelVersion>
     <parent>
         <groupId>org.thingsboard</groupId>
-        <version>2.0.1-SNAPSHOT</version>
+        <version>2.0.2-SNAPSHOT</version>
         <artifactId>rule-engine</artifactId>
     </parent>
     <groupId>org.thingsboard.rule-engine</groupId>
diff --git a/rule-engine/rule-engine-components/pom.xml b/rule-engine/rule-engine-components/pom.xml
index 03a5c23..1b37162 100644
--- a/rule-engine/rule-engine-components/pom.xml
+++ b/rule-engine/rule-engine-components/pom.xml
@@ -22,7 +22,7 @@
     <modelVersion>4.0.0</modelVersion>
     <parent>
         <groupId>org.thingsboard</groupId>
-        <version>2.0.1-SNAPSHOT</version>
+        <version>2.0.2-SNAPSHOT</version>
         <artifactId>rule-engine</artifactId>
     </parent>
     <groupId>org.thingsboard.rule-engine</groupId>
diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java
index c0a813c..6b05b7c 100644
--- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java
+++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java
@@ -113,7 +113,7 @@ public class TbMqttNode implements TbNode {
         }
         config.setCleanSession(this.config.isCleanSession());
         this.config.getCredentials().configure(config);
-        MqttClient client = MqttClient.create(config);
+        MqttClient client = MqttClient.create(config, null);
         client.setEventLoop(this.eventLoopGroup);
         Future<MqttConnectResult> connectFuture = client.connect(this.config.getHost(), this.config.getPort());
         MqttConnectResult result;

tools/pom.xml 2(+1 -1)

diff --git a/tools/pom.xml b/tools/pom.xml
index f9ae067..ea80d2e 100644
--- a/tools/pom.xml
+++ b/tools/pom.xml
@@ -20,7 +20,7 @@
     <modelVersion>4.0.0</modelVersion>
     <parent>
         <groupId>org.thingsboard</groupId>
-        <version>2.0.1-SNAPSHOT</version>
+        <version>2.0.2-SNAPSHOT</version>
         <artifactId>thingsboard</artifactId>
     </parent>
     <groupId>org.thingsboard</groupId>
diff --git a/transport/coap/pom.xml b/transport/coap/pom.xml
index 4874e4a..16df9aa 100644
--- a/transport/coap/pom.xml
+++ b/transport/coap/pom.xml
@@ -20,7 +20,7 @@
     <modelVersion>4.0.0</modelVersion>
     <parent>
         <groupId>org.thingsboard</groupId>
-        <version>2.0.1-SNAPSHOT</version>
+        <version>2.0.2-SNAPSHOT</version>
         <artifactId>transport</artifactId>
     </parent>
     <groupId>org.thingsboard.transport</groupId>
diff --git a/transport/http/pom.xml b/transport/http/pom.xml
index 154c2d9..288e850 100644
--- a/transport/http/pom.xml
+++ b/transport/http/pom.xml
@@ -20,7 +20,7 @@
     <modelVersion>4.0.0</modelVersion>
     <parent>
         <groupId>org.thingsboard</groupId>
-        <version>2.0.1-SNAPSHOT</version>
+        <version>2.0.2-SNAPSHOT</version>
         <artifactId>transport</artifactId>
     </parent>
     <groupId>org.thingsboard.transport</groupId>
diff --git a/transport/mqtt/pom.xml b/transport/mqtt/pom.xml
index 952aeea..8dd707a 100644
--- a/transport/mqtt/pom.xml
+++ b/transport/mqtt/pom.xml
@@ -20,7 +20,7 @@
     <modelVersion>4.0.0</modelVersion>
     <parent>
         <groupId>org.thingsboard</groupId>
-        <version>2.0.1-SNAPSHOT</version>
+        <version>2.0.2-SNAPSHOT</version>
         <artifactId>transport</artifactId>
     </parent>
     <groupId>org.thingsboard.transport</groupId>
diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java
index bf2aa0c..cc825b9 100644
--- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java
+++ b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java
@@ -155,6 +155,7 @@ public class GatewaySessionCtx {
             GatewayDeviceSessionCtx deviceSessionCtx = devices.get(deviceName);
             processor.process(new BasicTransportToDeviceSessionActorMsg(deviceSessionCtx.getDevice(),
                     new BasicAdaptorToSessionActorMsg(deviceSessionCtx, new ToDeviceRpcResponseMsg(requestId, data))));
+            ack(mqttMsg);
         } else {
             throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json);
         }
diff --git a/transport/pom.xml b/transport/pom.xml
index 5c78047..23e91eb 100644
--- a/transport/pom.xml
+++ b/transport/pom.xml
@@ -20,7 +20,7 @@
     <modelVersion>4.0.0</modelVersion>
     <parent>
         <groupId>org.thingsboard</groupId>
-        <version>2.0.1-SNAPSHOT</version>
+        <version>2.0.2-SNAPSHOT</version>
         <artifactId>thingsboard</artifactId>
     </parent>
     <groupId>org.thingsboard</groupId>

ui/package.json 2(+1 -1)

diff --git a/ui/package.json b/ui/package.json
index 85ee4b8..83bca57 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -1,7 +1,7 @@
 {
   "name": "thingsboard",
   "private": true,
-  "version": "2.0.1",
+  "version": "2.0.2",
   "description": "Thingsboard UI",
   "licenses": [
     {

ui/pom.xml 2(+1 -1)

diff --git a/ui/pom.xml b/ui/pom.xml
index 356a0eb..a6cabd8 100644
--- a/ui/pom.xml
+++ b/ui/pom.xml
@@ -20,7 +20,7 @@
     <modelVersion>4.0.0</modelVersion>
     <parent>
         <groupId>org.thingsboard</groupId>
-        <version>2.0.1-SNAPSHOT</version>
+        <version>2.0.2-SNAPSHOT</version>
         <artifactId>thingsboard</artifactId>
     </parent>
     <groupId>org.thingsboard</groupId>
diff --git a/ui/src/app/locale/locale.constant.js b/ui/src/app/locale/locale.constant.js
index 5bb599c..29f6f8e 100644
--- a/ui/src/app/locale/locale.constant.js
+++ b/ui/src/app/locale/locale.constant.js
@@ -512,7 +512,7 @@ export default angular.module('thingsboard.locale', [])
                     "configuration-error": "Configuration error",
                     "alias-resolution-error-title": "Dashboard aliases configuration error",
                     "invalid-aliases-config": "Unable to find any devices matching to some of the aliases filter.<br/>" +
-                                              "Please contact your administrator in order to resolve this issue.",
+                        "Please contact your administrator in order to resolve this issue.",
                     "select-devices": "Select devices",
                     "assignedToCustomer": "Assigned to customer",
                     "assignedToCustomers": "Assigned to customers",
@@ -897,13 +897,13 @@ export default angular.module('thingsboard.locale', [])
                     "opc-identity": "Identity",
                     "opc-keystore": "Keystore",
                     "opc-type": "Type",
-                    "opc-keystore-type":"Type",
-                    "opc-keystore-location":"Location *",
-                    "opc-keystore-password":"Password",
-                    "opc-keystore-alias":"Alias",
-                    "opc-keystore-key-password":"Key password",
-                    "opc-device-node-pattern":"Device node pattern",
-                    "opc-device-name-pattern":"Device name pattern",
+                    "opc-keystore-type": "Type",
+                    "opc-keystore-location": "Location *",
+                    "opc-keystore-password": "Password",
+                    "opc-keystore-alias": "Alias",
+                    "opc-keystore-key-password": "Key password",
+                    "opc-device-node-pattern": "Device node pattern",
+                    "opc-device-name-pattern": "Device name pattern",
                     "modbus-server": "Servers/slaves",
                     "modbus-add-server": "Add server/slave",
                     "modbus-add-server-prompt": "Please add server/slave",
@@ -918,7 +918,7 @@ export default angular.module('thingsboard.locale', [])
                     "modbus-stopbits-range": "Stop bits should be in a range from 1 to 2.",
                     "modbus-unit-id": "Unit ID",
                     "modbus-unit-id-range": "Unit ID should be in a range from 1 to 247.",
-                    "modbus-device-name":"Device name",
+                    "modbus-device-name": "Device name",
                     "modbus-poll-period": "Poll period (ms)",
                     "modbus-attributes-poll-period": "Attributes poll period (ms)",
                     "modbus-timeseries-poll-period": "Timeseries poll period (ms)",
@@ -941,8 +941,8 @@ export default angular.module('thingsboard.locale', [])
                         "not-available": "Not available"
                     },
 
-                    "export-extensions-configuration":"Export extensions configuration",
-                    "import-extensions-configuration":"Import extensions configuration",
+                    "export-extensions-configuration": "Export extensions configuration",
+                    "import-extensions-configuration": "Import extensions configuration",
                     "import-extensions": "Import extensions",
                     "import-extension": "Import extension",
                     "export-extension": "Export extension",
@@ -1445,14 +1445,6 @@ export default angular.module('thingsboard.locale', [])
                     "material-icons": "Material icons",
                     "show-all": "Show all icons"
                 },
-                "language": {
-                    "language": "Language",
-                    "en_US": "English",
-                    "ko_KR": "Korean",
-                    "zh_CN": "Chinese",
-                    "ru_RU": "Russian",
-                    "es_ES": "Spanish"
-                },
                 "custom": {
                     "widget-action": {
                         "action-cell-button": "Action cell button",
@@ -1460,6 +1452,14 @@ export default angular.module('thingsboard.locale', [])
                         "marker-click": "On marker click",
                         "tooltip-tag-action": "Tooltip tag action"
                     }
+                },
+                "language": {
+                    "language": "Language",
+                    "en_US": "English",
+                    "ko_KR": "Korean",
+                    "zh_CN": "Chinese",
+                    "ru_RU": "Russian",
+                    "es_ES": "Spanish"
                 }
             }
         }
diff --git a/ui/src/app/locale/locale.constant-es.js b/ui/src/app/locale/locale.constant-es.js
index af8b5b1..c6d2f96 100644
--- a/ui/src/app/locale/locale.constant-es.js
+++ b/ui/src/app/locale/locale.constant-es.js
@@ -13,804 +13,1321 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
- export default function addLocaleSpanish(locales) {
+export default function addLocaleSpanish(locales) {
     var es_ES = {
-          "access": {
-              "unauthorized": "No autorizado",
-              "unauthorized-access": "Acceso no autorizado",
-              "unauthorized-access-text": "Debes iniciar sesión para tener acceso a este recurso!",
-              "access-forbidden": "Acceso Prohibido",
-              "access-forbidden-text": "No tienes derechos para acceder a esta ubicación!<br/>Intenta iniciar sesión con otro usuario si todavía quieres acceder a esta ubicación.",
-              "refresh-token-expired": "La sesión ha expirado",
-              "refresh-token-failed": "No se puede actualizar la sesión"
+        "access": {
+            "unauthorized": "No autorizado",
+            "unauthorized-access": "Acceso no autorizado",
+            "unauthorized-access-text": "Debes iniciar sesión para tener acceso a este recurso!",
+            "access-forbidden": "Acceso Prohibido",
+            "access-forbidden-text": "No tienes derechos para acceder a esta ubicación!<br/>Intenta iniciar sesión con otro usuario si todavía quieres acceder a esta ubicación.",
+            "refresh-token-expired": "La sesión ha expirado",
+            "refresh-token-failed": "No se puede actualizar la sesión"
         },
         "action": {
-              "activate": "Activar", 
-              "suspend": "Suspender",
-              "save": "Guardar",
-              "saveAs": "Guardar como",
-              "cancel": "Cancelar",
-              "ok": "OK",
-              "delete": "Borrar",
-              "add": "Agregar",
-              "yes": "Si",
-              "no": "No",
-              "update": "Actualizar",
-              "remove": "Eliminar",
-              "search": "Buscar",
-              "assign": "Asignar",
-              "unassign": "Cancelar asignación",
-              "share": "Compartir",
-              "make-private": "Hacer privado",
-              "apply": "Aplicar",
-              "apply-changes": "Aplicar cambios",
-              "edit-mode": "Modo Edición",
-              "enter-edit-mode": "Modo Edición",
-              "decline-changes": "Descartar cambios",
-              "close": "Cerrar",
-              "back": "Atrás",
-              "run": "Correr",
-              "sign-in": "Regístrate!",
-              "edit": "Editar",
-              "view": "Ver",
-              "create": "Crear",
-              "drag": "Arrastrar",
-              "refresh": "Refrescar",
-              "undo": "Deshacer",
-              "copy": "Copiar",
-              "paste": "Pegar",
-              "import": "Importar",
-              "export": "Exportar",
-              "share-via": "Compartir vía {{provider}}"
+            "activate": "Activar",
+            "suspend": "Suspender",
+            "save": "Guardar",
+            "saveAs": "Guardar como",
+            "cancel": "Cancelar",
+            "ok": "OK",
+            "delete": "Borrar",
+            "add": "Agregar",
+            "yes": "Si",
+            "no": "No",
+            "update": "Actualizar",
+            "remove": "Eliminar",
+            "search": "Buscar",
+            "assign": "Asignar",
+            "unassign": "Cancelar asignación",
+            "share": "Compartir",
+            "make-private": "Hacer privado",
+            "apply": "Aplicar",
+            "apply-changes": "Aplicar cambios",
+            "edit-mode": "Modo Edición",
+            "enter-edit-mode": "Modo Edición",
+            "decline-changes": "Descartar cambios",
+            "close": "Cerrar",
+            "back": "Atrás",
+            "run": "Correr",
+            "sign-in": "Regístrate!",
+            "edit": "Editar",
+            "view": "Ver",
+            "create": "Crear",
+            "drag": "Arrastrar",
+            "refresh": "Refrescar",
+            "undo": "Deshacer",
+            "copy": "Copiar",
+            "paste": "Pegar",
+            "import": "Importar",
+            "export": "Exportar",
+            "share-via": "Compartir vía {{provider}}"
         },
         "aggregation": {
-              "aggregation": "Agregación",
-              "function": "Función de Agregación",
-              "limit": "Valores Max",
-              "group-interval": "Intervalo de agrupación",
-              "min": "Min",
-              "max": "Max",
-              "avg": "Promedio",
-              "sum": "Suma",
-              "count": "Cuenta",
-              "none": "Ninguno"
+            "aggregation": "Agregación",
+            "function": "Función de Agregación",
+            "limit": "Valores Max",
+            "group-interval": "Intervalo de agrupación",
+            "min": "Min",
+            "max": "Max",
+            "avg": "Promedio",
+            "sum": "Suma",
+            "count": "Cuenta",
+            "none": "Ninguno"
         },
         "admin": {
-              "general": "General",
-              "general-settings": "Ajustes General",
-              "outgoing-mail": "Mail de Salida",
-              "outgoing-mail-settings": "Ajustes del Mail de Salida",
-              "system-settings": "Sistema",
-              "test-mail-sent": "Mail de prueba enviado correctamente!",
-              "base-url": "URL Base",
-              "base-url-required": "URL Base requerida.",
-              "mail-from": "Mail Desde",
-              "mail-from-required": "Mail Desde requerido.",
-              "smtp-protocol": "Protocolo SMTP",
-              "smtp-host": "Host SMTP",
-              "smtp-host-required": "Host SMTP requerido.",
-              "smtp-port": "Puerto SMTP",
-              "smtp-port-required": "Debe ingresar un Puerto SMTP.",
-              "smtp-port-invalid": "No parece un Puerto SMTP valido.",
-              "timeout-msec": "Timeout (ms)",
-              "timeout-required": "Timeout requerido.",
-              "timeout-invalid": "No parece un Timeout valido.",
-              "enable-tls": "Habilitar TLS",
-              "send-test-mail": "Enviar mail de prueba"
+            "general": "General",
+            "general-settings": "Ajustes General",
+            "outgoing-mail": "Mail de Salida",
+            "outgoing-mail-settings": "Ajustes del Mail de Salida",
+            "system-settings": "Sistema",
+            "test-mail-sent": "Mail de prueba enviado correctamente!",
+            "base-url": "URL Base",
+            "base-url-required": "URL Base requerida.",
+            "mail-from": "Mail Desde",
+            "mail-from-required": "Mail Desde requerido.",
+            "smtp-protocol": "Protocolo SMTP",
+            "smtp-host": "Host SMTP",
+            "smtp-host-required": "Host SMTP requerido.",
+            "smtp-port": "Puerto SMTP",
+            "smtp-port-required": "Debe ingresar un Puerto SMTP.",
+            "smtp-port-invalid": "No parece un Puerto SMTP valido.",
+            "timeout-msec": "Timeout (ms)",
+            "timeout-required": "Timeout requerido.",
+            "timeout-invalid": "No parece un Timeout valido.",
+            "enable-tls": "Habilitar TLS",
+            "send-test-mail": "Enviar mail de prueba"
+        },
+        "alarm": { // TODO
+            "alarm": "Alarm",
+            "alarms": "Alarms",
+            "select-alarm": "Select alarm",
+            "no-alarms-matching": "No alarms matching '{{entity}}' were found.",
+            "alarm-required": "Alarm is required",
+            "alarm-status": "Alarm status",
+            "search-status": {
+                "ANY": "Any",
+                "ACTIVE": "Active",
+                "CLEARED": "Cleared",
+                "ACK": "Acknowledged",
+                "UNACK": "Unacknowledged"
+            },
+            "display-status": {
+                "ACTIVE_UNACK": "Active Unacknowledged",
+                "ACTIVE_ACK": "Active Acknowledged",
+                "CLEARED_UNACK": "Cleared Unacknowledged",
+                "CLEARED_ACK": "Cleared Acknowledged"
+            },
+            "no-alarms-prompt": "No alarms found",
+            "created-time": "Created time",
+            "type": "Type",
+            "severity": "Severity",
+            "originator": "Originator",
+            "originator-type": "Originator type",
+            "details": "Details",
+            "status": "Status",
+            "alarm-details": "Alarm details",
+            "start-time": "Start time",
+            "end-time": "End time",
+            "ack-time": "Acknowledged time",
+            "clear-time": "Cleared time",
+            "severity-critical": "Critical",
+            "severity-major": "Major",
+            "severity-minor": "Minor",
+            "severity-warning": "Warning",
+            "severity-indeterminate": "Indeterminate",
+            "acknowledge": "Acknowledge",
+            "clear": "Clear",
+            "search": "Search alarms",
+            "selected-alarms": "{ count, select, 1 {1 alarm} other {# alarms} } selected",
+            "no-data": "No data to display",
+            "polling-interval": "Alarms polling interval (sec)",
+            "polling-interval-required": "Alarms polling interval is required.",
+            "min-polling-interval-message": "At least 1 sec polling interval is allowed.",
+            "aknowledge-alarms-title": "Acknowledge { count, select, 1 {1 alarm} other {# alarms} }",
+            "aknowledge-alarms-text": "Are you sure you want to acknowledge { count, select, 1 {1 alarm} other {# alarms} }?",
+            "clear-alarms-title": "Clear { count, select, 1 {1 alarm} other {# alarms} }",
+            "clear-alarms-text": "Are you sure you want to clear { count, select, 1 {1 alarm} other {# alarms} }?"
+        },
+        "alias": { // TODO
+            "add": "Add alias",
+            "edit": "Edit alias",
+            "name": "Alias name",
+            "name-required": "Alias name is required",
+            "duplicate-alias": "Alias with same name is already exists.",
+            "filter-type-single-entity": "Single entity",
+            "filter-type-entity-list": "Entity list",
+            "filter-type-entity-name": "Entity name",
+            "filter-type-state-entity": "Entity from dashboard state",
+            "filter-type-state-entity-description": "Entity taken from dashboard state parameters",
+            "filter-type-asset-type": "Asset type",
+            "filter-type-asset-type-description": "Assets of type '{{assetType}}'",
+            "filter-type-asset-type-and-name-description": "Assets of type '{{assetType}}' and with name starting with '{{prefix}}'",
+            "filter-type-device-type": "Device type",
+            "filter-type-device-type-description": "Devices of type '{{deviceType}}'",
+            "filter-type-device-type-and-name-description": "Devices of type '{{deviceType}}' and with name starting with '{{prefix}}'",
+            "filter-type-relations-query": "Relations query",
+            "filter-type-relations-query-description": "{{entities}} that have {{relationType}} relation {{direction}} {{rootEntity}}",
+            "filter-type-asset-search-query": "Asset search query",
+            "filter-type-asset-search-query-description": "Assets with types {{assetTypes}} that have {{relationType}} relation {{direction}} {{rootEntity}}",
+            "filter-type-device-search-query": "Device search query",
+            "filter-type-device-search-query-description": "Devices with types {{deviceTypes}} that have {{relationType}} relation {{direction}} {{rootEntity}}",
+            "entity-filter": "Entity filter",
+            "resolve-multiple": "Resolve as multiple entities",
+            "filter-type": "Filter type",
+            "filter-type-required": "Filter type is required.",
+            "entity-filter-no-entity-matched": "No entities matching specified filter were found.",
+            "no-entity-filter-specified": "No entity filter specified",
+            "root-state-entity": "Use dashboard state entity as root",
+            "root-entity": "Root entity",
+            "state-entity-parameter-name": "State entity parameter name",
+            "default-state-entity": "Default state entity",
+            "default-entity-parameter-name": "By default",
+            "max-relation-level": "Max relation level",
+            "unlimited-level": "Unlimited level",
+            "state-entity": "Dashboard state entity",
+            "all-entities": "All entities",
+            "any-relation": "any"
+        },
+        "asset": { // TODO
+            "asset": "Asset",
+            "assets": "Assets",
+            "management": "Asset management",
+            "view-assets": "View Assets",
+            "add": "Add Asset",
+            "assign-to-customer": "Assign to customer",
+            "assign-asset-to-customer": "Assign Asset(s) To Customer",
+            "assign-asset-to-customer-text": "Please select the assets to assign to the customer",
+            "no-assets-text": "No assets found",
+            "assign-to-customer-text": "Please select the customer to assign the asset(s)",
+            "public": "Public",
+            "assignedToCustomer": "Assigned to customer",
+            "make-public": "Make asset public",
+            "make-private": "Make asset private",
+            "unassign-from-customer": "Unassign from customer",
+            "delete": "Delete asset",
+            "asset-public": "Asset is public",
+            "asset-type": "Asset type",
+            "asset-type-required": "Asset type is required.",
+            "select-asset-type": "Select asset type",
+            "enter-asset-type": "Enter asset type",
+            "any-asset": "Any asset",
+            "no-asset-types-matching": "No asset types matching '{{entitySubtype}}' were found.",
+            "asset-type-list-empty": "No asset types selected.",
+            "asset-types": "Asset types",
+            "name": "Name",
+            "name-required": "Name is required.",
+            "description": "Description",
+            "type": "Type",
+            "type-required": "Type is required.",
+            "details": "Details",
+            "events": "Events",
+            "add-asset-text": "Add new asset",
+            "asset-details": "Asset details",
+            "assign-assets": "Assign assets",
+            "assign-assets-text": "Assign { count, select, 1 {1 asset} other {# assets} } to customer",
+            "delete-assets": "Delete assets",
+            "unassign-assets": "Unassign assets",
+            "unassign-assets-action-title": "Unassign { count, select, 1 {1 asset} other {# assets} } from customer",
+            "assign-new-asset": "Assign new asset",
+            "delete-asset-title": "Are you sure you want to delete the asset '{{assetName}}'?",
+            "delete-asset-text": "Be careful, after the confirmation the asset and all related data will become unrecoverable.",
+            "delete-assets-title": "Are you sure you want to delete { count, select, 1 {1 asset} other {# assets} }?",
+            "delete-assets-action-title": "Delete { count, select, 1 {1 asset} other {# assets} }",
+            "delete-assets-text": "Be careful, after the confirmation all selected assets will be removed and all related data will become unrecoverable.",
+            "make-public-asset-title": "Are you sure you want to make the asset '{{assetName}}' public?",
+            "make-public-asset-text": "After the confirmation the asset and all its data will be made public and accessible by others.",
+            "make-private-asset-title": "Are you sure you want to make the asset '{{assetName}}' private?",
+            "make-private-asset-text": "After the confirmation the asset and all its data will be made private and won't be accessible by others.",
+            "unassign-asset-title": "Are you sure you want to unassign the asset '{{assetName}}'?",
+            "unassign-asset-text": "After the confirmation the asset will be unassigned and won't be accessible by the customer.",
+            "unassign-asset": "Unassign asset",
+            "unassign-assets-title": "Are you sure you want to unassign { count, select, 1 {1 asset} other {# assets} }?",
+            "unassign-assets-text": "After the confirmation all selected assets will be unassigned and won't be accessible by the customer.",
+            "copyId": "Copy asset Id",
+            "idCopiedMessage": "Asset Id has been copied to clipboard",
+            "select-asset": "Select asset",
+            "no-assets-matching": "No assets matching '{{entity}}' were found.",
+            "asset-required": "Asset is required",
+            "name-starts-with": "Asset name starts with"
         },
         "attribute": {
-              "attributes": "Atributos",
-              "latest-telemetry": "Última telemetría",
-              "attributes-scope": "Alcance de los atributos del dispositivo",
-              "scope-latest-telemetry": "Última telemetría",
-              "scope-client": "Atributos del Cliente",
-              "scope-server": "Atributos del Servidor",
-              "scope-shared": "Atributos Compartidos",
-              "add": "Agregar atributo",
-              "key": "Clave",
-              "key-required": "Clave del atributo requerida.",
-              "value": "Valor",
-              "value-required": "Valor del atributo requerido.",
-              "delete-attributes-title": "¿Estás seguro que quieres eliminar { count, select, 1 {1 atributo} other {# atributos} }?",
-              "delete-attributes-text": "Ten cuidado, luego de confirmar el atributo será eliminado, y la información relacionada será irrecuperable.",
-              "delete-attributes": "Borrar atributo",
-              "enter-attribute-value": "Ingresar valor del atributo",
-              "show-on-widget": "Mostrar en Widget",
-              "widget-mode": "Widget",
-              "next-widget": "Widget siguiente",
-              "prev-widget": "Widget anterior",
-              "add-to-dashboard": "Agregar al Panel",
-              "add-widget-to-dashboard": "Agregar widget al Panel",
-              "selected-attributes": "{ count, select, 1 {1 atributo} other {# atributos} } seleccionados",
-              "selected-telemetry": "{ count, select, 1 {1 unidad de telemetría } other {# unidades de telemetría} } seleccionadas."
+            "attributes": "Atributos",
+            "latest-telemetry": "Última telemetría",
+            "attributes-scope": "Alcance de los atributos del dispositivo",
+            "scope-latest-telemetry": "Última telemetría",
+            "scope-client": "Atributos del Cliente",
+            "scope-server": "Atributos del Servidor",
+            "scope-shared": "Atributos Compartidos",
+            "add": "Agregar atributo",
+            "key": "Clave",
+            "key-required": "Clave del atributo requerida.",
+            "value": "Valor",
+            "value-required": "Valor del atributo requerido.",
+            "delete-attributes-title": "¿Estás seguro que quieres eliminar { count, select, 1 {1 atributo} other {# atributos} }?",
+            "delete-attributes-text": "Ten cuidado, luego de confirmar el atributo será eliminado, y la información relacionada será irrecuperable.",
+            "delete-attributes": "Borrar atributo",
+            "enter-attribute-value": "Ingresar valor del atributo",
+            "show-on-widget": "Mostrar en Widget",
+            "widget-mode": "Widget",
+            "next-widget": "Widget siguiente",
+            "prev-widget": "Widget anterior",
+            "add-to-dashboard": "Agregar al Panel",
+            "add-widget-to-dashboard": "Agregar widget al Panel",
+            "selected-attributes": "{ count, select, 1 {1 atributo} other {# atributos} } seleccionados",
+            "selected-telemetry": "{ count, select, 1 {1 unidad de telemetría } other {# unidades de telemetría} } seleccionadas."
+        },
+        "audit-log": { // TODO
+            "audit": "Audit",
+            "audit-logs": "Audit Logs",
+            "timestamp": "Timestamp",
+            "entity-type": "Entity Type",
+            "entity-name": "Entity Name",
+            "user": "User",
+            "type": "Type",
+            "status": "Status",
+            "details": "Details",
+            "type-added": "Added",
+            "type-deleted": "Deleted",
+            "type-updated": "Updated",
+            "type-attributes-updated": "Attributes updated",
+            "type-attributes-deleted": "Attributes deleted",
+            "type-rpc-call": "RPC call",
+            "type-credentials-updated": "Credentials updated",
+            "type-assigned-to-customer": "Assigned to Customer",
+            "type-unassigned-from-customer": "Unassigned from Customer",
+            "type-activated": "Activated",
+            "type-suspended": "Suspended",
+            "type-credentials-read": "Credentials read",
+            "type-attributes-read": "Attributes read",
+            "status-success": "Success",
+            "status-failure": "Failure",
+            "audit-log-details": "Audit log details",
+            "no-audit-logs-prompt": "No logs found",
+            "action-data": "Action data",
+            "failure-details": "Failure details",
+            "search": "Search audit logs",
+            "clear-search": "Clear search"
         },
         "confirm-on-exit": {
-              "message": "Tienes cambios sin guardar. ¿Estás seguro que quieres abandonar la página?",
-              "html-message": "Tienes cambios sin guardar.<br/>¿Estás seguro que quieres abandonar la página?",
-              "title": "Cambios sin guardar"
+            "message": "Tienes cambios sin guardar. ¿Estás seguro que quieres abandonar la página?",
+            "html-message": "Tienes cambios sin guardar.<br/>¿Estás seguro que quieres abandonar la página?",
+            "title": "Cambios sin guardar"
         },
         "contact": {
-              "country": "País",
-              "city": "Ciudad",
-              "state": "Estado/Provincia",
-              "postal-code": "Código Postal",
-              "postal-code-invalid": "Solo se permiten dígitos.",
-              "address": "Dirección",
-              "address2": "Dirección 2",
-              "phone": "Teléfono",
-              "email": "Email",
-              "no-address": "Sin Dirección"
+            "country": "País",
+            "city": "Ciudad",
+            "state": "Estado/Provincia",
+            "postal-code": "Código Postal",
+            "postal-code-invalid": "Solo se permiten dígitos.",
+            "address": "Dirección",
+            "address2": "Dirección 2",
+            "phone": "Teléfono",
+            "email": "Email",
+            "no-address": "Sin Dirección"
         },
         "common": {
-              "username": "Usuario",
-              "password": "Contraseña",
-              "enter-username": "Ingresa el nombre de usuario.",
-              "enter-password": "Ingresa la contraseña",
-              "enter-search": "Ingresa búsqueda"
+            "username": "Usuario",
+            "password": "Contraseña",
+            "enter-username": "Ingresa el nombre de usuario.",
+            "enter-password": "Ingresa la contraseña",
+            "enter-search": "Ingresa búsqueda"
+        },
+        "content-type": { // TODO
+            "json": "Json",
+            "text": "Text",
+            "binary": "Binary (Base64)"
         },
         "customer": {
-              "customers": "Clientes",
-              "management": "Gestión de Clientes",
-              "dashboard": "Panel del Cliente",
-              "dashboards": "Paneles del Cliente",
-              "devices": "Panel del Cliente",
-              "public-dashboards": "Paneles Públicos",
-              "public-devices": "Dispositivos Públicos",
-              "add": "Agregar cliente",
-              "delete": "Borrar cliente",
-              "manage-customer-users": "Gestionar usuarios del cliente",
-              "manage-customer-devices": "Gestionar dispositivos del cliente",
-              "manage-customer-dashboards": "Gestionar paneles del cliente",
-              "manage-public-devices": "Gestionar dispositivos públicos",
-              "manage-public-dashboards": "Gestionar paneles públicos",
-              "add-customer-text": "Agregar nuevo cliente",
-              "no-customers-text": "No se encontrar clientes",
-              "customer-details": "Detalles del cliente",
-              "delete-customer-title": "¿Estás seguro que quieres eliminar el cliente '{{customerTitle}}'?",
-              "delete-customer-text": "Ten cuidado, luego de confirmar el cliente será eliminado y toda la información relacionada será irrecuperable.",
-              "delete-customers-title": "¿Estás seguro que quieres eliminar { count, select, 1 {1 cliente} other {# clientes} }?",
-              "delete-customers-action-title": "Borrar { count, select, 1 {1 cliente} other {# clientes} }",
-              "delete-customers-text": "Ten cuidado, luego de confirmar todos los clientes seleccionados serán eliminados y su información relacionada será irrecuperable.",
-              "manage-users": "Gestionar usuarios",
-              "manage-devices": "Gestionar dispositivos",
-              "manage-dashboards": "Gestionar paneles",
-              "title": "Título",
-              "title-required": "Título requerido.",
-              "description": "Descripción"
+            "customers": "Clientes",
+            "management": "Gestión de Clientes",
+            "dashboard": "Panel del Cliente",
+            "dashboards": "Paneles del Cliente",
+            "devices": "Panel del Cliente",
+            "public-dashboards": "Paneles Públicos",
+            "public-devices": "Dispositivos Públicos",
+            "add": "Agregar cliente",
+            "delete": "Borrar cliente",
+            "manage-customer-users": "Gestionar usuarios del cliente",
+            "manage-customer-devices": "Gestionar dispositivos del cliente",
+            "manage-customer-dashboards": "Gestionar paneles del cliente",
+            "manage-public-devices": "Gestionar dispositivos públicos",
+            "manage-public-dashboards": "Gestionar paneles públicos",
+            "add-customer-text": "Agregar nuevo cliente",
+            "no-customers-text": "No se encontrar clientes",
+            "customer-details": "Detalles del cliente",
+            "delete-customer-title": "¿Estás seguro que quieres eliminar el cliente '{{customerTitle}}'?",
+            "delete-customer-text": "Ten cuidado, luego de confirmar el cliente será eliminado y toda la información relacionada será irrecuperable.",
+            "delete-customers-title": "¿Estás seguro que quieres eliminar { count, select, 1 {1 cliente} other {# clientes} }?",
+            "delete-customers-action-title": "Borrar { count, select, 1 {1 cliente} other {# clientes} }",
+            "delete-customers-text": "Ten cuidado, luego de confirmar todos los clientes seleccionados serán eliminados y su información relacionada será irrecuperable.",
+            "manage-users": "Gestionar usuarios",
+            "manage-devices": "Gestionar dispositivos",
+            "manage-dashboards": "Gestionar paneles",
+            "title": "Título",
+            "title-required": "Título requerido.",
+            "description": "Descripción"
         },
         "datetime": {
-              "date-from": "Fecha desde",
-              "time-from": "Tiempo desde",
-              "date-to": "Fecha hasta",
-              "time-to": "Tiempo hasta"
+            "date-from": "Fecha desde",
+            "time-from": "Tiempo desde",
+            "date-to": "Fecha hasta",
+            "time-to": "Tiempo hasta"
         },
         "dashboard": {
-              "dashboard": "Panel",
-              "dashboards": "Paneles",
-              "management": "Gestión de Paneles",
-              "view-dashboards": "Ver paneles",
-              "add": "Agregar Panel",
-              "assign-dashboard-to-customer": "Asignar panel(es) a cliente",
-              "assign-dashboard-to-customer-text": "Por favor, seleccione algún panel para asignar al Cliente.",
-              "assign-to-customer-text": "Por favor, seleccione algún cliente para asignar al(los) panel(es).",
-              "assign-to-customer": "Asignar a cliente",
-              "unassign-from-customer": "Desasignar del cliente",
-              "make-public": "Hacer panel público",
-              "make-private": "Hacer panel privado",
-              "no-dashboards-text": "Ningún panel encontrado",
-              "no-widgets": "Ningún widget configurado",
-              "add-widget": "Agregar nuevo widget",
-              "title": "Titulo",
-              "select-widget-title": "Seleccionar widget",
-              "select-widget-subtitle": "Lista de tipos de widgets",
-              "delete": "Eliminar panel",
-              "title-required": "Título requerido.",
-              "description": "Descripción",
-              "details": "Detalles",
-              "dashboard-details": "Detalles del panel",
-              "add-dashboard-text": "Agregar nuevo panel",
-              "assign-dashboards": "Asignar paneles",
-              "assign-new-dashboard": "Asignar nuevo panel",
-              "assign-dashboards-text": "Asignar { count, select, 1 {1 panel} other {# paneles} } al cliente",
-              "delete-dashboards": "Eliminar paneles",
-              "unassign-dashboards": "Desasignar paneles",
-              "unassign-dashboards-action-title": "Desasignar { count, select, 1 {1 paneles} other {# paneles} } del cliente",
-              "delete-dashboard-title": "¿Estás seguro que quieres eliminar el panel '{{dashboardTitle}}'?",
-              "delete-dashboard-text": "Ten cuidado, el panel seleccionado será eliminado y la información relacionada sera irrecuperable.",
-              "delete-dashboards-title": "¿Estás seguro que quieres eliminar { count, select, 1 {1 panel} other {# paneles} }?",
-              "delete-dashboards-action-title": "Eliminar { count, select, 1 {1 panel} other {# paneles} }",
-              "delete-dashboards-text": "Ten cuidado, los paneles seleccionados serán eliminados y la información relacionada será irrecuperable.",
-              "unassign-dashboard-title": "¿Estás seguro que quieres desasignar el panel '{{dashboardTitle}}'?",
-              "unassign-dashboard-text": "Luego de confirmar, el panel será desasignado y no podrá ser accesible por el cliente.",
-              "unassign-dashboard": "Desasignar panel",
-              "unassign-dashboards-title": "¿Estás seguro que quieres desasignar { count, select, 1 {1 panel} other {# paneles} }?",
-              "unassign-dashboards-text": "Luego de confirmar, los paneles seleccionados serán desasignados y no podrán ser accesibles por el cliente.",
-              "public-dashboard-title": "El panel ahora es público",
-              "public-dashboard-text": "Tu panel <b>{{dashboardTitle}}</b> es ahora público y podrá ser accedido desde: <a href='{{publicLink}}' target='_blank'>aquí</a>:",
-              "public-dashboard-notice": "<b>Nota:</b>  No olvides hacer públicos los dispositivos relacionados para acceder a sus datos.",
-              "make-private-dashboard-title": "¿Estás seguro que quieres hacer el panel '{{dashboardTitle}}' privado?",
-              "make-private-dashboard-text": "Luego de confirmar, el panel será privado y no podrá ser accesible por otros.",
-              "make-private-dashboard": "Hacer panel privado",
-              "socialshare-text": "'{{dashboardTitle}}' powered by ThingsBoard",
-              "socialshare-title": "'{{dashboardTitle}}' powered by ThingsBoard",
-              "select-dashboard": "Seleccionar panel",
-              "no-dashboards-matching": "Panel '{{entity}}' no encontrado.",
-              "dashboard-required": "Panel requerido.",
-              "select-existing": "Seleccionar paneles existentes",
-              "create-new": "Crear nuevo panel",
-              "new-dashboard-title": "Nuevo título",
-              "open-dashboard": "Abrir panel",
-              "set-background": "Definir fondo",
-              "background-color": "Color de fondo",
-              "background-image": "Imagen de fondo",
-              "background-size-mode": "Modo tamaño de fondo",
-              "no-image": "No se ha seleccionado ningúna imagen",
-              "drop-image": "Suelte una imagen o haga clic para seleccionar un archivo para cargar.",
-              "settings": "Ajustes",
-              "columns-count": "Número de columnas",
-              "columns-count-required": "Número de columnas requerido.",
-              "min-columns-count-message": "Solo se permite un número mínimo de 10 columnas.",
-              "max-columns-count-message": "Solo se permite un número máximo de 1000 columnas.",
-              "widgets-margins": "Margen entre widgets",
-              "horizontal-margin": "Margen horizontal",
-              "horizontal-margin-required": "Margen horizontal requerido.",
-              "min-horizontal-margin-message": "Solo se permite margen horizontal mínimo de 0.",
-              "max-horizontal-margin-message": "Solo se permite margen horizontal máximo de 50.",
-              "vertical-margin": "Margen vertical",
-              "vertical-margin-required": "Margen vertical requerido.",
-              "min-vertical-margin-message": "Solo se permite margen vertical mínimo de 0.",
-              "max-vertical-margin-message": "Solo se permite margen vertical máximo de 50.",
-              "display-title": "Mostrar título del panel",
-              "title-color": "Color del título",
-              "display-device-selection": "Mostrar selección de dispositivo",
-              "display-dashboard-timewindow": "Mostrar ventana de tiempo",
-              "display-dashboard-export": "Mostrar exportar",
-              "import": "Importar panel",
-              "export": "Exportar panel",
-              "export-failed-error": "Imposible exportar panel: {{error}}",
-              "create-new-dashboard": "Crear nuevo panel",
-              "dashboard-file": "Archivo del panel",
-              "invalid-dashboard-file-error": "Imposible importar panel: Estructura de datos inválida.",
-              "dashboard-import-missing-aliases-title": "Configurar alias utilizados por el panel importado",
-              "create-new-widget": "Crear nuevo widget",
-              "import-widget": "Importar widget",
-              "widget-file": "Archivo de widget",
-              "invalid-widget-file-error": "Imposible importar widget: Estructura de datos inválida.",
-              "widget-import-missing-aliases-title": "Configurar alias utilizados por el widget",
-              "open-toolbar": "Abrir toolbar del panel",
-              "close-toolbar": "Cerrar toolbar",
-              "configuration-error": "Error de configuración",
-              "alias-resolution-error-title": "Error de configuración de alias del panel",
-              "invalid-aliases-config": "No se puede encontrar ningún dispositivo que coincida con algunos de los alias de filtro.<br/>" +
-              "Póngase en contacto con su administrador para resolver este problema.",
-              "select-devices": "Seleccionar dispositivos",
-              "assignedToCustomer": "Asignado al cliente",
-              "public": "Público",
-              "public-link": "Link público",
-              "copy-public-link": "Copiar link público",
-              "public-link-copied-message": "El link público del panel se ha copiado al portapapeles"
+            "dashboard": "Panel",
+            "dashboards": "Paneles",
+            "management": "Gestión de Paneles",
+            "view-dashboards": "Ver paneles",
+            "add": "Agregar Panel",
+            "assign-dashboard-to-customer": "Asignar panel(es) a cliente",
+            "assign-dashboard-to-customer-text": "Por favor, seleccione algún panel para asignar al Cliente.",
+            "assign-to-customer-text": "Por favor, seleccione algún cliente para asignar al(los) panel(es).",
+            "assign-to-customer": "Asignar a cliente",
+            "unassign-from-customer": "Desasignar del cliente",
+            "make-public": "Hacer panel público",
+            "make-private": "Hacer panel privado",
+            "no-dashboards-text": "Ningún panel encontrado",
+            "no-widgets": "Ningún widget configurado",
+            "add-widget": "Agregar nuevo widget",
+            "title": "Titulo",
+            "select-widget-title": "Seleccionar widget",
+            "select-widget-subtitle": "Lista de tipos de widgets",
+            "delete": "Eliminar panel",
+            "title-required": "Título requerido.",
+            "description": "Descripción",
+            "details": "Detalles",
+            "dashboard-details": "Detalles del panel",
+            "add-dashboard-text": "Agregar nuevo panel",
+            "assign-dashboards": "Asignar paneles",
+            "assign-new-dashboard": "Asignar nuevo panel",
+            "assign-dashboards-text": "Asignar { count, select, 1 {1 panel} other {# paneles} } al cliente",
+            "delete-dashboards": "Eliminar paneles",
+            "unassign-dashboards": "Desasignar paneles",
+            "unassign-dashboards-action-title": "Desasignar { count, select, 1 {1 paneles} other {# paneles} } del cliente",
+            "delete-dashboard-title": "¿Estás seguro que quieres eliminar el panel '{{dashboardTitle}}'?",
+            "delete-dashboard-text": "Ten cuidado, el panel seleccionado será eliminado y la información relacionada sera irrecuperable.",
+            "delete-dashboards-title": "¿Estás seguro que quieres eliminar { count, select, 1 {1 panel} other {# paneles} }?",
+            "delete-dashboards-action-title": "Eliminar { count, select, 1 {1 panel} other {# paneles} }",
+            "delete-dashboards-text": "Ten cuidado, los paneles seleccionados serán eliminados y la información relacionada será irrecuperable.",
+            "unassign-dashboard-title": "¿Estás seguro que quieres desasignar el panel '{{dashboardTitle}}'?",
+            "unassign-dashboard-text": "Luego de confirmar, el panel será desasignado y no podrá ser accesible por el cliente.",
+            "unassign-dashboard": "Desasignar panel",
+            "unassign-dashboards-title": "¿Estás seguro que quieres desasignar { count, select, 1 {1 panel} other {# paneles} }?",
+            "unassign-dashboards-text": "Luego de confirmar, los paneles seleccionados serán desasignados y no podrán ser accesibles por el cliente.",
+            "public-dashboard-title": "El panel ahora es público",
+            "public-dashboard-text": "Tu panel <b>{{dashboardTitle}}</b> es ahora público y podrá ser accedido desde: <a href='{{publicLink}}' target='_blank'>aquí</a>:",
+            "public-dashboard-notice": "<b>Nota:</b>  No olvides hacer públicos los dispositivos relacionados para acceder a sus datos.",
+            "make-private-dashboard-title": "¿Estás seguro que quieres hacer el panel '{{dashboardTitle}}' privado?",
+            "make-private-dashboard-text": "Luego de confirmar, el panel será privado y no podrá ser accesible por otros.",
+            "make-private-dashboard": "Hacer panel privado",
+            "socialshare-text": "'{{dashboardTitle}}' powered by ThingsBoard",
+            "socialshare-title": "'{{dashboardTitle}}' powered by ThingsBoard",
+            "select-dashboard": "Seleccionar panel",
+            "no-dashboards-matching": "Panel '{{entity}}' no encontrado.",
+            "dashboard-required": "Panel requerido.",
+            "select-existing": "Seleccionar paneles existentes",
+            "create-new": "Crear nuevo panel",
+            "new-dashboard-title": "Nuevo título",
+            "open-dashboard": "Abrir panel",
+            "set-background": "Definir fondo",
+            "background-color": "Color de fondo",
+            "background-image": "Imagen de fondo",
+            "background-size-mode": "Modo tamaño de fondo",
+            "no-image": "No se ha seleccionado ningúna imagen",
+            "drop-image": "Suelte una imagen o haga clic para seleccionar un archivo para cargar.",
+            "settings": "Ajustes",
+            "columns-count": "Número de columnas",
+            "columns-count-required": "Número de columnas requerido.",
+            "min-columns-count-message": "Solo se permite un número mínimo de 10 columnas.",
+            "max-columns-count-message": "Solo se permite un número máximo de 1000 columnas.",
+            "widgets-margins": "Margen entre widgets",
+            "horizontal-margin": "Margen horizontal",
+            "horizontal-margin-required": "Margen horizontal requerido.",
+            "min-horizontal-margin-message": "Solo se permite margen horizontal mínimo de 0.",
+            "max-horizontal-margin-message": "Solo se permite margen horizontal máximo de 50.",
+            "vertical-margin": "Margen vertical",
+            "vertical-margin-required": "Margen vertical requerido.",
+            "min-vertical-margin-message": "Solo se permite margen vertical mínimo de 0.",
+            "max-vertical-margin-message": "Solo se permite margen vertical máximo de 50.",
+            "display-title": "Mostrar título del panel",
+            "title-color": "Color del título",
+            "display-device-selection": "Mostrar selección de dispositivo",
+            "display-dashboard-timewindow": "Mostrar ventana de tiempo",
+            "display-dashboard-export": "Mostrar exportar",
+            "import": "Importar panel",
+            "export": "Exportar panel",
+            "export-failed-error": "Imposible exportar panel: {{error}}",
+            "create-new-dashboard": "Crear nuevo panel",
+            "dashboard-file": "Archivo del panel",
+            "invalid-dashboard-file-error": "Imposible importar panel: Estructura de datos inválida.",
+            "dashboard-import-missing-aliases-title": "Configurar alias utilizados por el panel importado",
+            "create-new-widget": "Crear nuevo widget",
+            "import-widget": "Importar widget",
+            "widget-file": "Archivo de widget",
+            "invalid-widget-file-error": "Imposible importar widget: Estructura de datos inválida.",
+            "widget-import-missing-aliases-title": "Configurar alias utilizados por el widget",
+            "open-toolbar": "Abrir toolbar del panel",
+            "close-toolbar": "Cerrar toolbar",
+            "configuration-error": "Error de configuración",
+            "alias-resolution-error-title": "Error de configuración de alias del panel",
+            "invalid-aliases-config": "No se puede encontrar ningún dispositivo que coincida con algunos de los alias de filtro.<br/>" +
+                "Póngase en contacto con su administrador para resolver este problema.",
+            "select-devices": "Seleccionar dispositivos",
+            "assignedToCustomer": "Asignado al cliente",
+            "public": "Público",
+            "public-link": "Link público",
+            "copy-public-link": "Copiar link público",
+            "public-link-copied-message": "El link público del panel se ha copiado al portapapeles"
         },
         "datakey": {
-              "settings": "Ajustes",
-              "advanced": "Avanzado",
-              "label": "Etiqueta",
-              "color": "Color",
-              "data-generation-func": "Función de generación de datos",
-              "use-data-post-processing-func": "Usar funcíon de post-procesamiendo de datos",
-              "configuration": "Ajustes de clave de datos",
-              "timeseries": "Serie de tiempos",
-              "attributes": "Atributos",
-              "timeseries-required": "Series de tiempo del dispositivo requerido.",
-              "timeseries-or-attributes-required": "Series de tiempo/Atributos requeridos.",
-              "function-types": "Tipos de funciones",
-              "function-types-required": "Tipos de funciones requerido."
+            "settings": "Ajustes",
+            "advanced": "Avanzado",
+            "label": "Etiqueta",
+            "color": "Color",
+            "data-generation-func": "Función de generación de datos",
+            "use-data-post-processing-func": "Usar funcíon de post-procesamiendo de datos",
+            "configuration": "Ajustes de clave de datos",
+            "timeseries": "Serie de tiempos",
+            "attributes": "Atributos",
+            "timeseries-required": "Series de tiempo del dispositivo requerido.",
+            "timeseries-or-attributes-required": "Series de tiempo/Atributos requeridos.",
+            "function-types": "Tipos de funciones",
+            "function-types-required": "Tipos de funciones requerido."
         },
         "datasource": {
-              "type": "Típo de fuente de datos",
-              "add-datasource-prompt": "Por favor, agrega una fuente de datos"
+            "type": "Típo de fuente de datos",
+            "add-datasource-prompt": "Por favor, agrega una fuente de datos"
         },
         "details": {
-              "edit-mode": "Modo Edición",
-              "toggle-edit-mode": "Ir a Modo Edición"
+            "edit-mode": "Modo Edición",
+            "toggle-edit-mode": "Ir a Modo Edición"
         },
         "device": {
-              "device": "Dispositivo",
-              "device-required": "Dispositivo requerido.",
-              "devices": "Dispositivos",
-              "management": "Gestión de Dispositivos",
-              "view-devices": "Ver dispositivos",
-              "device-alias": "Alias de dispositivo",
-              "aliases": "Alias de dispositivos",
-              "no-alias-matching": "'{{alias}}' no encontrado.",
-              "no-aliases-found": "Ningún alias encontrado.",
-              "no-key-matching": "'{{key}}' no encontrado.",
-              "no-keys-found": "Ninguna clave encontrada.",
-              "create-new-alias": "Crear nuevo alias!",
-              "create-new-key": "Crear nueva clave!",
-              "duplicate-alias-error": "Alias duplicado '{{alias}}'.<br> El alias de los dispositivos deben ser únicos dentro del panel.",
-              "configure-alias": "Configurar alias '{{alias}}'",
-              "no-devices-matching": "No se encontró dispositivo '{{entity}}'",
-              "alias": "Alias",
-              "alias-required": "Alias de dispositivo requerido.",
-              "remove-alias": "Eliminar alias",
-              "add-alias": "Agregar alias",
-              "name-starts-with": "Nombre empieza con",
-              "device-list": "Lista de dispositivos",
-              "use-device-name-filter": "Usar filtro",
-              "device-list-empty": "Ningún dispositivo seleccionado.",
-              "device-name-filter-required": "Nombre de filtro requerido.",
-              "device-name-filter-no-device-matched": "Ningún dispositivo encontrado que comience con '{{device}}'.",
-              "add": "Agregar dispositivo",
-              "assign-to-customer": "Asignar a cliente",
-              "assign-device-to-customer": "Asignar dispositivo(s) a Cliente",
-              "assign-device-to-customer-text": "Por favor, seleccione los dispositivos que serán asignados al cliente",
-              "make-public": "Hacer dispositivo público",
-              "make-private": "Hacer dispositivo privado",
-              "no-devices-text": "Ningún dispositivo encontrado",
-              "assign-to-customer-text": "Por favor, seleccione el cliente para asignar el(los) dispositivo(s)",
-              "device-details": "Detalles del dispositivo",
-              "add-device-text": "Agregar nuevo dispositivo",
-              "credentials": "Credenciales",
-              "manage-credentials": "Gestionar credenciales",
-              "delete": "Eliminar dispositivo",
-              "assign-devices": "Asignar dispositivo",
-              "assign-devices-text": "Asignar { count, select, 1 {1 dispositivo} other {# dispositivos} } al cliente",
-              "delete-devices": "Eliminar dispositivo",
-              "unassign-from-customer": "Desasignar del cliente",
-              "unassign-devices": "Desasignar dispositivos",
-              "unassign-devices-action-title": "Desasignar { count, select, 1 {1 dispositivo} other {# dispositivos} } del cliente",
-              "assign-new-device": "Asignar nuevo dispositivo",
-              "make-public-device-title": "¿Estás seguro que quieres hacer el dispositivo '{{deviceName}}' público?",
-              "make-public-device-text": "Luego de confirmar, el dispositivo y la información relacionada serán públicos y podrá ser accesible por otros.",
-              "make-private-device-title": "¿Estás seguro que quieres hacer el dispositivo '{{deviceName}}' privado?",
-              "make-private-device-text": "Luego de confirmar, el dispositivo y la información relacionada serán privados y no podrá ser accesible por otros.",
-              "view-credentials": "Ver credenciales",
-              "delete-device-title": "¿Estás seguro que quieres eliminar el dispositivo '{{deviceName}}'?",
-              "delete-device-text": "Ten cuidado, luego de confirmar los dispositivos serán eliminados y la información relacionada será irrecuperable.",
-              "delete-devices-title": "¿Estás seguro que quieres eliminar { count, select, 1 {1 dispositivo} other {# dispositivos} }?",
-              "delete-devices-action-title": "Eliminar { count, select, 1 {1 dispositivo} other {# dispositivos} }",
-              "delete-devices-text": "Ten cuidado, luego de confirmar los dispositivos seleccionados serán eliminados y la información relacionada será irrecuperable.",
-              "unassign-device-title": "¿Estás seguro que quieres desasignar el dispositivo '{{deviceName}}'?",
-              "unassign-device-text": "Luego de confirmar el dispositivo será desasignado y no podrá ser accesible por el cliente.",
-              "unassign-device": "Desasignar dispositivo",
-              "unassign-devices-title": "¿Estás seguro que quieres desasignar { count, select, 1 {1 dispositivo} other {# dispositivos} }?",
-              "unassign-devices-text": "Luego de confirmar los dispositivos seleccionados serán desasignados y no podrán ser accedidos por el cliente.",
-              "device-credentials": "Credenciales del dispositivo",
-              "credentials-type": "Tipo de credencial",
-              "access-token": "Access token",
-              "access-token-required": "Access token requerido.",
-              "access-token-invalid": "Access token debe tener entre 1 a 20 caracteres.",
-              "rsa-key": "Clave pública RSA",
-              "rsa-key-required": "Clave pública RSA requerida.",
-              "secret": "Secreta",
-              "secret-required": "Secreta requerida.",
-              "name": "Nombre",
-              "name-required": "Nombre requerido.",
-              "description": "Descripción",
-              "events": "Eventos",
-              "details": "Detalles",
-              "copyId": "Copiar ID",
-              "copyAccessToken": "Copiar access token",
-              "idCopiedMessage": "Id del dispositivo copiado al portapapeles",
-              "accessTokenCopiedMessage": "Access token del dispositivo copiado al portapapeles",
-              "assignedToCustomer": "Asignado al cliente",
-              "unable-delete-device-alias-title": "Imposible eliminar alias del dispositivo",
-              "unable-delete-device-alias-text": "Alias '{{deviceAlias}}' no puede ser eliminado. Esta siendo usado por el(los) widget(s):<br/>{{widgetsList}}",
-              "is-gateway": "Es gateway",
-              "public": "Público",
-              "device-public": "Dispositivo público"
+            "device": "Dispositivo",
+            "device-required": "Dispositivo requerido.",
+            "devices": "Dispositivos",
+            "management": "Gestión de Dispositivos",
+            "view-devices": "Ver dispositivos",
+            "device-alias": "Alias de dispositivo",
+            "aliases": "Alias de dispositivos",
+            "no-alias-matching": "'{{alias}}' no encontrado.",
+            "no-aliases-found": "Ningún alias encontrado.",
+            "no-key-matching": "'{{key}}' no encontrado.",
+            "no-keys-found": "Ninguna clave encontrada.",
+            "create-new-alias": "Crear nuevo alias!",
+            "create-new-key": "Crear nueva clave!",
+            "duplicate-alias-error": "Alias duplicado '{{alias}}'.<br> El alias de los dispositivos deben ser únicos dentro del panel.",
+            "configure-alias": "Configurar alias '{{alias}}'",
+            "no-devices-matching": "No se encontró dispositivo '{{entity}}'",
+            "alias": "Alias",
+            "alias-required": "Alias de dispositivo requerido.",
+            "remove-alias": "Eliminar alias",
+            "add-alias": "Agregar alias",
+            "name-starts-with": "Nombre empieza con",
+            "device-list": "Lista de dispositivos",
+            "use-device-name-filter": "Usar filtro",
+            "device-list-empty": "Ningún dispositivo seleccionado.",
+            "device-name-filter-required": "Nombre de filtro requerido.",
+            "device-name-filter-no-device-matched": "Ningún dispositivo encontrado que comience con '{{device}}'.",
+            "add": "Agregar dispositivo",
+            "assign-to-customer": "Asignar a cliente",
+            "assign-device-to-customer": "Asignar dispositivo(s) a Cliente",
+            "assign-device-to-customer-text": "Por favor, seleccione los dispositivos que serán asignados al cliente",
+            "make-public": "Hacer dispositivo público",
+            "make-private": "Hacer dispositivo privado",
+            "no-devices-text": "Ningún dispositivo encontrado",
+            "assign-to-customer-text": "Por favor, seleccione el cliente para asignar el(los) dispositivo(s)",
+            "device-details": "Detalles del dispositivo",
+            "add-device-text": "Agregar nuevo dispositivo",
+            "credentials": "Credenciales",
+            "manage-credentials": "Gestionar credenciales",
+            "delete": "Eliminar dispositivo",
+            "assign-devices": "Asignar dispositivo",
+            "assign-devices-text": "Asignar { count, select, 1 {1 dispositivo} other {# dispositivos} } al cliente",
+            "delete-devices": "Eliminar dispositivo",
+            "unassign-from-customer": "Desasignar del cliente",
+            "unassign-devices": "Desasignar dispositivos",
+            "unassign-devices-action-title": "Desasignar { count, select, 1 {1 dispositivo} other {# dispositivos} } del cliente",
+            "assign-new-device": "Asignar nuevo dispositivo",
+            "make-public-device-title": "¿Estás seguro que quieres hacer el dispositivo '{{deviceName}}' público?",
+            "make-public-device-text": "Luego de confirmar, el dispositivo y la información relacionada serán públicos y podrá ser accesible por otros.",
+            "make-private-device-title": "¿Estás seguro que quieres hacer el dispositivo '{{deviceName}}' privado?",
+            "make-private-device-text": "Luego de confirmar, el dispositivo y la información relacionada serán privados y no podrá ser accesible por otros.",
+            "view-credentials": "Ver credenciales",
+            "delete-device-title": "¿Estás seguro que quieres eliminar el dispositivo '{{deviceName}}'?",
+            "delete-device-text": "Ten cuidado, luego de confirmar los dispositivos serán eliminados y la información relacionada será irrecuperable.",
+            "delete-devices-title": "¿Estás seguro que quieres eliminar { count, select, 1 {1 dispositivo} other {# dispositivos} }?",
+            "delete-devices-action-title": "Eliminar { count, select, 1 {1 dispositivo} other {# dispositivos} }",
+            "delete-devices-text": "Ten cuidado, luego de confirmar los dispositivos seleccionados serán eliminados y la información relacionada será irrecuperable.",
+            "unassign-device-title": "¿Estás seguro que quieres desasignar el dispositivo '{{deviceName}}'?",
+            "unassign-device-text": "Luego de confirmar el dispositivo será desasignado y no podrá ser accesible por el cliente.",
+            "unassign-device": "Desasignar dispositivo",
+            "unassign-devices-title": "¿Estás seguro que quieres desasignar { count, select, 1 {1 dispositivo} other {# dispositivos} }?",
+            "unassign-devices-text": "Luego de confirmar los dispositivos seleccionados serán desasignados y no podrán ser accedidos por el cliente.",
+            "device-credentials": "Credenciales del dispositivo",
+            "credentials-type": "Tipo de credencial",
+            "access-token": "Access token",
+            "access-token-required": "Access token requerido.",
+            "access-token-invalid": "Access token debe tener entre 1 a 20 caracteres.",
+            "rsa-key": "Clave pública RSA",
+            "rsa-key-required": "Clave pública RSA requerida.",
+            "secret": "Secreta",
+            "secret-required": "Secreta requerida.",
+            "name": "Nombre",
+            "name-required": "Nombre requerido.",
+            "description": "Descripción",
+            "events": "Eventos",
+            "details": "Detalles",
+            "copyId": "Copiar ID",
+            "copyAccessToken": "Copiar access token",
+            "idCopiedMessage": "Id del dispositivo copiado al portapapeles",
+            "accessTokenCopiedMessage": "Access token del dispositivo copiado al portapapeles",
+            "assignedToCustomer": "Asignado al cliente",
+            "unable-delete-device-alias-title": "Imposible eliminar alias del dispositivo",
+            "unable-delete-device-alias-text": "Alias '{{deviceAlias}}' no puede ser eliminado. Esta siendo usado por el(los) widget(s):<br/>{{widgetsList}}",
+            "is-gateway": "Es gateway",
+            "public": "Público",
+            "device-public": "Dispositivo público"
         },
         "dialog": {
-              "close": "Cerrar cuadro de diálogo"
+            "close": "Cerrar cuadro de diálogo"
         },
         "error": {
-              "unable-to-connect": "Imposible conectar con el servidor! Por favor, revise su conexión a internet.",
-              "unhandled-error-code": "Código de error no manejado: {{errorCode}}",
-              "unknown-error": "Error desconocido"
+            "unable-to-connect": "Imposible conectar con el servidor! Por favor, revise su conexión a internet.",
+            "unhandled-error-code": "Código de error no manejado: {{errorCode}}",
+            "unknown-error": "Error desconocido"
+        },
+        "entity": { // TODO
+            "entity": "Entity",
+            "entities": "Entities",
+            "aliases": "Entity aliases",
+            "entity-alias": "Entity alias",
+            "unable-delete-entity-alias-title": "Unable to delete entity alias",
+            "unable-delete-entity-alias-text": "Entity alias '{{entityAlias}}' can't be deleted as it used by the following widget(s):<br/>{{widgetsList}}",
+            "duplicate-alias-error": "Duplicate alias found '{{alias}}'.<br>Entity aliases must be unique whithin the dashboard.",
+            "missing-entity-filter-error": "Filter is missing for alias '{{alias}}'.",
+            "configure-alias": "Configure '{{alias}}' alias",
+            "alias": "Alias",
+            "alias-required": "Entity alias is required.",
+            "remove-alias": "Remove entity alias",
+            "add-alias": "Add entity alias",
+            "entity-list": "Entity list",
+            "entity-type": "Entity type",
+            "entity-types": "Entity types",
+            "entity-type-list": "Entity type list",
+            "any-entity": "Any entity",
+            "enter-entity-type": "Enter entity type",
+            "no-entities-matching": "No entities matching '{{entity}}' were found.",
+            "no-entity-types-matching": "No entity types matching '{{entityType}}' were found.",
+            "name-starts-with": "Name starts with",
+            "use-entity-name-filter": "Use filter",
+            "entity-list-empty": "No entities selected.",
+            "entity-type-list-empty": "No entity types selected.",
+            "entity-name-filter-required": "Entity name filter is required.",
+            "entity-name-filter-no-entity-matched": "No entities starting with '{{entity}}' were found.",
+            "all-subtypes": "All",
+            "select-entities": "Select entities",
+            "no-aliases-found": "No aliases found.",
+            "no-alias-matching": "'{{alias}}' not found.",
+            "create-new-alias": "Create a new one!",
+            "key": "Key",
+            "key-name": "Key name",
+            "no-keys-found": "No keys found.",
+            "no-key-matching": "'{{key}}' not found.",
+            "create-new-key": "Create a new one!",
+            "type": "Type",
+            "type-required": "Entity type is required.",
+            "type-device": "Device",
+            "type-devices": "Devices",
+            "list-of-devices": "{ count, select, 1 {One device} other {List of # devices} }",
+            "device-name-starts-with": "Devices whose names start with '{{prefix}}'",
+            "type-asset": "Asset",
+            "type-assets": "Assets",
+            "list-of-assets": "{ count, select, 1 {One asset} other {List of # assets} }",
+            "asset-name-starts-with": "Assets whose names start with '{{prefix}}'",
+            "type-rule": "Rule",
+            "type-rules": "Rules",
+            "list-of-rules": "{ count, select, 1 {One rule} other {List of # rules} }",
+            "rule-name-starts-with": "Rules whose names start with '{{prefix}}'",
+            "type-plugin": "Plugin",
+            "type-plugins": "Plugins",
+            "list-of-plugins": "{ count, select, 1 {One plugin} other {List of # plugins} }",
+            "plugin-name-starts-with": "Plugins whose names start with '{{prefix}}'",
+            "type-tenant": "Tenant",
+            "type-tenants": "Tenants",
+            "list-of-tenants": "{ count, select, 1 {One tenant} other {List of # tenants} }",
+            "tenant-name-starts-with": "Tenants whose names start with '{{prefix}}'",
+            "type-customer": "Customer",
+            "type-customers": "Customers",
+            "list-of-customers": "{ count, select, 1 {One customer} other {List of # customers} }",
+            "customer-name-starts-with": "Customers whose names start with '{{prefix}}'",
+            "type-user": "User",
+            "type-users": "Users",
+            "list-of-users": "{ count, select, 1 {One user} other {List of # users} }",
+            "user-name-starts-with": "Users whose names start with '{{prefix}}'",
+            "type-dashboard": "Dashboard",
+            "type-dashboards": "Dashboards",
+            "list-of-dashboards": "{ count, select, 1 {One dashboard} other {List of # dashboards} }",
+            "dashboard-name-starts-with": "Dashboards whose names start with '{{prefix}}'",
+            "type-alarm": "Alarm",
+            "type-alarms": "Alarms",
+            "list-of-alarms": "{ count, select, 1 {One alarms} other {List of # alarms} }",
+            "alarm-name-starts-with": "Alarms whose names start with '{{prefix}}'",
+            "type-rulechain": "Rule chain",
+            "type-rulechains": "Rule chains",
+            "list-of-rulechains": "{ count, select, 1 {One rule chain} other {List of # rule chains} }",
+            "rulechain-name-starts-with": "Rule chains whose names start with '{{prefix}}'",
+            "type-current-customer": "Current Customer",
+            "search": "Search entities",
+            "selected-entities": "{ count, select, 1 {1 entity} other {# entities} } selected",
+            "entity-name": "Entity name",
+            "details": "Entity details",
+            "no-entities-prompt": "No entities found",
+            "no-data": "No data to display"
         },
         "event": {
-              "event-type": "Tipo de evento",
-              "type-error": "Error",
-              "type-lc-event": "Ciclo de vida",
-              "type-stats": "Estadísticas",
-              "no-events-prompt": "Ningún evento encontrado.",
-              "error": "Error",
-              "alarm": "Alarma",
-              "event-time": "Hora del evento",
-              "server": "Servidor",
-              "body": "Cuerpo",
-              "method": "Método",
-              "event": "Evento",
-              "status": "Status",
-              "success": "Éxito",
-              "failed": "Fallo",
-              "messages-processed": "Mensajes procesados",
-              "errors-occurred": "Ocurrieron errores"
+            "event-type": "Tipo de evento",
+            "type-error": "Error",
+            "type-lc-event": "Ciclo de vida",
+            "type-stats": "Estadísticas",
+            "no-events-prompt": "Ningún evento encontrado.",
+            "error": "Error",
+            "alarm": "Alarma",
+            "event-time": "Hora del evento",
+            "server": "Servidor",
+            "body": "Cuerpo",
+            "method": "Método",
+            "event": "Evento",
+            "status": "Status",
+            "success": "Éxito",
+            "failed": "Fallo",
+            "messages-processed": "Mensajes procesados",
+            "errors-occurred": "Ocurrieron errores"
+        },
+        "extension": { // TODO
+            "extensions": "Extensions",
+            "selected-extensions": "{ count, select, 1 {1 extension} other {# extensions} } selected",
+            "type": "Type",
+            "key": "Key",
+            "value": "Value",
+            "id": "Id",
+            "extension-id": "Extension id",
+            "extension-type": "Extension type",
+            "transformer-json": "JSON *",
+            "unique-id-required": "Current extension id already exists.",
+            "delete": "Delete extension",
+            "add": "Add extension",
+            "edit": "Edit extension",
+            "delete-extension-title": "Are you sure you want to delete the extension '{{extensionId}}'?",
+            "delete-extension-text": "Be careful, after the confirmation the extension and all related data will become unrecoverable.",
+            "delete-extensions-title": "Are you sure you want to delete { count, select, 1 {1 extension} other {# extensions} }?",
+            "delete-extensions-text": "Be careful, after the confirmation all selected extensions will be removed.",
+            "converters": "Converters",
+            "converter-id": "Converter id",
+            "configuration": "Configuration",
+            "converter-configurations": "Converter configurations",
+            "token": "Security token",
+            "add-converter": "Add converter",
+            "add-config": "Add converter configuration",
+            "device-name-expression": "Device name expression",
+            "device-type-expression": "Device type expression",
+            "custom": "Custom",
+            "to-double": "To Double",
+            "transformer": "Transformer",
+            "json-required": "Transformer json is required.",
+            "json-parse": "Unable to parse transformer json.",
+            "attributes": "Attributes",
+            "add-attribute": "Add attribute",
+            "add-map": "Add mapping element",
+            "timeseries": "Timeseries",
+            "add-timeseries": "Add timeseries",
+            "field-required": "Field is required",
+            "brokers": "Brokers",
+            "add-broker": "Add broker",
+            "host": "Host",
+            "port": "Port",
+            "port-range": "Port should be in a range from 1 to 65535.",
+            "ssl": "Ssl",
+            "credentials": "Credentials",
+            "username": "Username",
+            "password": "Password",
+            "retry-interval": "Retry interval in milliseconds",
+            "anonymous": "Anonymous",
+            "basic": "Basic",
+            "pem": "PEM",
+            "ca-cert": "CA certificate file *",
+            "private-key": "Private key file *",
+            "cert": "Certificate file *",
+            "no-file": "No file selected.",
+            "drop-file": "Drop a file or click to select a file to upload.",
+            "mapping": "Mapping",
+            "topic-filter": "Topic filter",
+            "converter-type": "Converter type",
+            "converter-json": "Json",
+            "json-name-expression": "Device name json expression",
+            "topic-name-expression": "Device name topic expression",
+            "json-type-expression": "Device type json expression",
+            "topic-type-expression": "Device type topic expression",
+            "attribute-key-expression": "Attribute key expression",
+            "attr-json-key-expression": "Attribute key json expression",
+            "attr-topic-key-expression": "Attribute key topic expression",
+            "request-id-expression": "Request id expression",
+            "request-id-json-expression": "Request id json expression",
+            "request-id-topic-expression": "Request id topic expression",
+            "response-topic-expression": "Response topic expression",
+            "value-expression": "Value expression",
+            "topic": "Topic",
+            "timeout": "Timeout in milliseconds",
+            "converter-json-required": "Converter json is required.",
+            "converter-json-parse": "Unable to parse converter json.",
+            "filter-expression": "Filter expression",
+            "connect-requests": "Connect requests",
+            "add-connect-request": "Add connect request",
+            "disconnect-requests": "Disconnect requests",
+            "add-disconnect-request": "Add disconnect request",
+            "attribute-requests": "Attribute requests",
+            "add-attribute-request": "Add attribute request",
+            "attribute-updates": "Attribute updates",
+            "add-attribute-update": "Add attribute update",
+            "server-side-rpc": "Server side RPC",
+            "add-server-side-rpc-request": "Add server-side RPC request",
+            "device-name-filter": "Device name filter",
+            "attribute-filter": "Attribute filter",
+            "method-filter": "Method filter",
+            "request-topic-expression": "Request topic expression",
+            "response-timeout": "Response timeout in milliseconds",
+            "topic-expression": "Topic expression",
+            "client-scope": "Client scope",
+            "add-device": "Add device",
+            "opc-server": "Servers",
+            "opc-add-server": "Add server",
+            "opc-add-server-prompt": "Please add server",
+            "opc-application-name": "Application name",
+            "opc-application-uri": "Application uri",
+            "opc-scan-period-in-seconds": "Scan period in seconds",
+            "opc-security": "Security",
+            "opc-identity": "Identity",
+            "opc-keystore": "Keystore",
+            "opc-type": "Type",
+            "opc-keystore-type": "Type",
+            "opc-keystore-location": "Location *",
+            "opc-keystore-password": "Password",
+            "opc-keystore-alias": "Alias",
+            "opc-keystore-key-password": "Key password",
+            "opc-device-node-pattern": "Device node pattern",
+            "opc-device-name-pattern": "Device name pattern",
+            "modbus-server": "Servers/slaves",
+            "modbus-add-server": "Add server/slave",
+            "modbus-add-server-prompt": "Please add server/slave",
+            "modbus-transport": "Transport",
+            "modbus-port-name": "Serial port name",
+            "modbus-encoding": "Encoding",
+            "modbus-parity": "Parity",
+            "modbus-baudrate": "Baud rate",
+            "modbus-databits": "Data bits",
+            "modbus-stopbits": "Stop bits",
+            "modbus-databits-range": "Data bits should be in a range from 7 to 8.",
+            "modbus-stopbits-range": "Stop bits should be in a range from 1 to 2.",
+            "modbus-unit-id": "Unit ID",
+            "modbus-unit-id-range": "Unit ID should be in a range from 1 to 247.",
+            "modbus-device-name": "Device name",
+            "modbus-poll-period": "Poll period (ms)",
+            "modbus-attributes-poll-period": "Attributes poll period (ms)",
+            "modbus-timeseries-poll-period": "Timeseries poll period (ms)",
+            "modbus-poll-period-range": "Poll period should be positive value.",
+            "modbus-tag": "Tag",
+            "modbus-function": "Function",
+            "modbus-register-address": "Register address",
+            "modbus-register-address-range": "Register address should be in a range from 0 to 65535.",
+            "modbus-register-bit-index": "Bit index",
+            "modbus-register-bit-index-range": "Bit index should be in a range from 0 to 15.",
+            "modbus-register-count": "Register count",
+            "modbus-register-count-range": "Register count should be a positive value.",
+            "modbus-byte-order": "Byte order",
+
+            "sync": {
+                "status": "Status",
+                "sync": "Sync",
+                "not-sync": "Not sync",
+                "last-sync-time": "Last sync time",
+                "not-available": "Not available"
+            },
+
+            "export-extensions-configuration": "Export extensions configuration",
+            "import-extensions-configuration": "Import extensions configuration",
+            "import-extensions": "Import extensions",
+            "import-extension": "Import extension",
+            "export-extension": "Export extension",
+            "file": "Extensions file",
+            "invalid-file-error": "Invalid extension file"
         },
         "fullscreen": {
-              "expand": "Expandir a Pantalla Completa",
-              "exit": "Salir de Pantalla Completa",
-              "toggle": "Cambiar el modo de Pantalla Completa",
-              "fullscreen": "Pantalla Completa"
+            "expand": "Expandir a Pantalla Completa",
+            "exit": "Salir de Pantalla Completa",
+            "toggle": "Cambiar el modo de Pantalla Completa",
+            "fullscreen": "Pantalla Completa"
         },
         "function": {
-              "function": "Función"
+            "function": "Función"
         },
         "grid": {
-              "delete-item-title": "¿Estás seguro que quieres eliminar este item?",
-              "delete-item-text": "Ten cuidado, luego de confirmar el item será eliminado y la información relacionada será irrecuperable.",
-              "delete-items-title": "¿Estás seguro que quieres eliminar { count, select, 1 {1 item} other {# items} }?",
-              "delete-items-action-title": "Eliminar { count, select, 1 {1 item} other {# items} }",
-              "delete-items-text": "Ten cuidado, luego de confirmar los items seleccionados serán eliminados y la información relacionada será irrecuperable.",
-              "add-item-text": "Agregar nuevo item",
-              "no-items-text": "Ningún item encontrado",
-              "item-details": "Detalles del item",
-              "delete-item": "Borrar Item",
-              "delete-items": "Borrar Items",
-              "scroll-to-top": "Ir hacia arriba"
+            "delete-item-title": "¿Estás seguro que quieres eliminar este item?",
+            "delete-item-text": "Ten cuidado, luego de confirmar el item será eliminado y la información relacionada será irrecuperable.",
+            "delete-items-title": "¿Estás seguro que quieres eliminar { count, select, 1 {1 item} other {# items} }?",
+            "delete-items-action-title": "Eliminar { count, select, 1 {1 item} other {# items} }",
+            "delete-items-text": "Ten cuidado, luego de confirmar los items seleccionados serán eliminados y la información relacionada será irrecuperable.",
+            "add-item-text": "Agregar nuevo item",
+            "no-items-text": "Ningún item encontrado",
+            "item-details": "Detalles del item",
+            "delete-item": "Borrar Item",
+            "delete-items": "Borrar Items",
+            "scroll-to-top": "Ir hacia arriba"
         },
         "help": {
-              "goto-help-page": "Ir a Página de Ayuda"
+            "goto-help-page": "Ir a Página de Ayuda"
         },
         "home": {
-              "home": "Principal",
-              "profile": "Perfil",
-              "logout": "Salir",
-              "menu": "Menu",
-              "avatar": "Avatar",
-              "open-user-menu": "Abrir menú de usuario"
+            "home": "Principal",
+            "profile": "Perfil",
+            "logout": "Salir",
+            "menu": "Menu",
+            "avatar": "Avatar",
+            "open-user-menu": "Abrir menú de usuario"
         },
         "import": {
-              "no-file": "Ningún archivo seleccionado",
-              "drop-file": "Arrastra un archivo JSON o clickea para seleccionar uno."
+            "no-file": "Ningún archivo seleccionado",
+            "drop-file": "Arrastra un archivo JSON o clickea para seleccionar uno."
         },
         "item": {
-              "selected": "Seleccionado"
+            "selected": "Seleccionado"
         },
         "js-func": {
-              "no-return-error": "La función debe retornar un valor!",
-              "return-type-mismatch": "La función debe retornar un valor de tipo: '{{type}}'!"
+            "no-return-error": "La función debe retornar un valor!",
+            "return-type-mismatch": "La función debe retornar un valor de tipo: '{{type}}'!"
+        },
+        "key-val": { // TODO
+            "key": "Key",
+            "value": "Value",
+            "remove-entry": "Remove entry",
+            "add-entry": "Add entry",
+            "no-data": "No entries"
+        },
+        "layout": { // TODO
+            "layout": "Layout",
+            "manage": "Manage layouts",
+            "settings": "Layout settings",
+            "color": "Color",
+            "main": "Main",
+            "right": "Right",
+            "select": "Select target layout"
         },
         "legend": {
-              "position": "Posición de leyenda",
-              "show-max": "Mostrar máximo",
-              "show-min": "Mostrar mínimo",
-              "show-avg": "Mostrar promedio",
-              "show-total": "Mostrar total",
-              "settings": "Ajustes de leyenda.",
-              "min": "min",
-              "max": "max",
-              "avg": "prom",
-              "total": "total"
+            "position": "Posición de leyenda",
+            "show-max": "Mostrar máximo",
+            "show-min": "Mostrar mínimo",
+            "show-avg": "Mostrar promedio",
+            "show-total": "Mostrar total",
+            "settings": "Ajustes de leyenda.",
+            "min": "min",
+            "max": "max",
+            "avg": "prom",
+            "total": "total"
         },
         "login": {
-              "login": "Ingresar",
-              "request-password-reset": "Pedir restablecer contraseña",
-              "reset-password": "Restablecer contraseña",
-              "create-password": "Crear contraseña",
-              "passwords-mismatch-error": "Las contraseñas deben ser las mismas!",
-              "password-again": "Reingresa la contraseña",
-              "sign-in": "Iniciar sesión",
-              "username": "Usuario (email)",
-              "remember-me": "Recordar",
-              "forgot-password": "¿Olvidaste tu contraseña?",
-              "password-reset": "Restablecer Contraseña",
-              "new-password": "Nueva contraseña",
-              "new-password-again": "Repita la nueva contraseña",
-              "password-link-sent-message": "Se ha enviado el enlace de restablecimiento de contraseña con éxito!",
-              "email": "Email"
-        },
-        "plugin": {
-              "plugins": "Plugins",
-              "delete": "Eliminar plugin",
-              "activate": "Activar plugin",
-              "suspend": "Suspender plugin",
-              "active": "Activo",
-              "suspended": "Suspendido",
-              "name": "Nombre",
-              "name-required": "Nombre requerido.",
-              "description": "Descripción",
-              "add": "Agregar Plugin",
-              "delete-plugin-title": "¿Estás seguro que quieres eliminar el plugin '{{pluginName}}'?",
-              "delete-plugin-text": "Ten cuidado, luego de confirmar el plugin será eliminado y la información relacionada será irrecuperable.",
-              "delete-plugins-title": "¿Estás seguro que quieres eliminar { count, select, 1 {1 plugin} other {# plugins} }?",
-              "delete-plugins-action-title": "Eliminar { count, select, 1 {1 plugin} other {# plugins} }",
-              "delete-plugins-text": "Ten cuidado, luego de confirmar todos los plugins seleccionados serán eliminados y la información relacionada será irrecuperable.",
-              "add-plugin-text": "Agregar nuevo plugin",
-              "no-plugins-text": "Ningún plugin encontrado",
-              "plugin-details": "Detalles",
-              "api-token": "API token",
-              "api-token-required": "API token requerido.",
-              "type": "Tipo del plugin",
-              "type-required": "Tipo requerido.",
-              "configuration": "Ajustes del plugin",
-              "system": "Sistema",
-              "select-plugin": "plugin",
-              "plugin": "Plugin",
-              "no-plugins-matching": "No se encontraron plugins: '{{entity}}'",
-              "plugin-required": "Plugin requerido.",
-              "plugin-require-match": "Por favor, elija un plugin existente.",
-              "events": "Eventos",
-              "details": "Detalles",
-              "import": "Importar plugin",
-              "export": "Exportar plugin",
-              "export-failed-error": "Imposible exportar plugin: {{error}}",
-              "create-new-plugin": "Crear nuevo plugin",
-              "plugin-file": "Archivo",
-              "invalid-plugin-file-error": "Imposible de importar plugin: Estructura de datos inválida."
+            "login": "Ingresar",
+            "request-password-reset": "Pedir restablecer contraseña",
+            "reset-password": "Restablecer contraseña",
+            "create-password": "Crear contraseña",
+            "passwords-mismatch-error": "Las contraseñas deben ser las mismas!",
+            "password-again": "Reingresa la contraseña",
+            "sign-in": "Iniciar sesión",
+            "username": "Usuario (email)",
+            "remember-me": "Recordar",
+            "forgot-password": "¿Olvidaste tu contraseña?",
+            "password-reset": "Restablecer Contraseña",
+            "new-password": "Nueva contraseña",
+            "new-password-again": "Repita la nueva contraseña",
+            "password-link-sent-message": "Se ha enviado el enlace de restablecimiento de contraseña con éxito!",
+            "email": "Email"
         },
         "position": {
-              "top": "Arriba",
-              "bottom": "Abajo",
-              "left": "Izquierda",
-              "right": "Derecha"
+            "top": "Arriba",
+            "bottom": "Abajo",
+            "left": "Izquierda",
+            "right": "Derecha"
         },
         "profile": {
-              "profile": "Perfil",
-              "change-password": "Cambiar contraseña",
-              "current-password": "Contraseña actual"
-        },
-        "rule": {
-              "rules": "Reglas",
-              "delete": "Eliminar regla",
-              "activate": "Activar regla",
-              "suspend": "Suspender regla",
-              "active": "Activada",
-              "suspended": "Suspendida",
-              "name": "Nombre",
-              "name-required": "Nombre requerido.",
-              "description": "Descripción",
-              "add": "Agregar Regla",
-              "delete-rule-title": "¿Estás seguro que quieres eliminar la regla '{{ruleName}}'?",
-              "delete-rule-text": "Ten cuidado, luego de confirmar la regla será eliminada y la información relacionada será irrecuperable.",
-              "delete-rules-title": "¿Estás seguro que quieres eliminar { count, select, 1 {1 regla} other {# reglas} }?",
-              "delete-rules-action-title": "Eliminar { count, select, 1 {1 regla} other {# reglas} }",
-              "delete-rules-text": "Ten cuidado, luego de confirmar todas las reglas seleccionadas serán borradas y la información relacionada será irrecuperable.",
-              "add-rule-text": "Agregar nueva regla",
-              "no-rules-text": "Ninguna regla encontrada",
-              "rule-details": "Detalles",
-              "filters": "Filtros",
-              "filter": "Filtro",
-              "add-filter-prompt": "Por favor, ingresa un filtro",
-              "remove-filter": "Eliminar filtro",
-              "add-filter": "Agregar filtro",
-              "filter-name": "Nombre",
-              "filter-type": "Tipo",
-              "edit-filter": "Editar filtro",
-              "view-filter": "Ver filtro",
-              "component-name": "Nombre",
-              "component-name-required": "Nombre requerido.",
-              "component-type": "Tipo",
-              "component-type-required": "Tipo requerido.",
-              "processor": "Procesador",
-              "no-processor-configured": "Ningún procesador encontrado",
-              "create-processor": "Crear procesador",
-              "processor-name": "Nombre",
-              "processor-type": "Tipo",
-              "plugin-action": "Acción del Plugin",
-              "action-name": "Nombre",
-              "action-type": "Tipo",
-              "create-action-prompt": "Por favor, crea una acción.",
-              "create-action": "Crear acción",
-              "details": "Detalles",
-              "events": "Eventos",
-              "system": "Sistema",
-              "import": "Importar regla",
-              "export": "Exportar regla",
-              "export-failed-error": "Imposible de exportar regla: {{error}}",
-              "create-new-rule": "Crear nueva regla",
-              "rule-file": "Archivo",
-              "invalid-rule-file-error": "Imposible de importar regla: Estructura de datos inválida."
-        },
-        "rule-plugin": {
-              "management": "Gestión de Reglas y Plugins"
+            "profile": "Perfil",
+            "change-password": "Cambiar contraseña",
+            "current-password": "Contraseña actual"
+        },
+        "relation": { // TODO
+            "relations": "Relations",
+            "direction": "Direction",
+            "search-direction": {
+                "FROM": "From",
+                "TO": "To"
+            },
+            "direction-type": {
+                "FROM": "from",
+                "TO": "to"
+            },
+            "from-relations": "Outbound relations",
+            "to-relations": "Inbound relations",
+            "selected-relations": "{ count, select, 1 {1 relation} other {# relations} } selected",
+            "type": "Type",
+            "to-entity-type": "To entity type",
+            "to-entity-name": "To entity name",
+            "from-entity-type": "From entity type",
+            "from-entity-name": "From entity name",
+            "to-entity": "To entity",
+            "from-entity": "From entity",
+            "delete": "Delete relation",
+            "relation-type": "Relation type",
+            "relation-type-required": "Relation type is required.",
+            "any-relation-type": "Any type",
+            "add": "Add relation",
+            "edit": "Edit relation",
+            "delete-to-relation-title": "Are you sure you want to delete relation to the entity '{{entityName}}'?",
+            "delete-to-relation-text": "Be careful, after the confirmation the entity '{{entityName}}' will be unrelated from the current entity.",
+            "delete-to-relations-title": "Are you sure you want to delete { count, select, 1 {1 relation} other {# relations} }?",
+            "delete-to-relations-text": "Be careful, after the confirmation all selected relations will be removed and corresponding entities will be unrelated from the current entity.",
+            "delete-from-relation-title": "Are you sure you want to delete relation from the entity '{{entityName}}'?",
+            "delete-from-relation-text": "Be careful, after the confirmation current entity will be unrelated from the entity '{{entityName}}'.",
+            "delete-from-relations-title": "Are you sure you want to delete { count, select, 1 {1 relation} other {# relations} }?",
+            "delete-from-relations-text": "Be careful, after the confirmation all selected relations will be removed and current entity will be unrelated from the corresponding entities.",
+            "remove-relation-filter": "Remove relation filter",
+            "add-relation-filter": "Add relation filter",
+            "any-relation": "Any relation",
+            "relation-filters": "Relation filters",
+            "additional-info": "Additional info (JSON)",
+            "invalid-additional-info": "Unable to parse additional info json."
+        },
+        "rulechain": { // TODO
+            "rulechain": "Rule chain",
+            "rulechains": "Rule chains",
+            "root": "Root",
+            "delete": "Delete rule chain",
+            "name": "Name",
+            "name-required": "Name is required.",
+            "description": "Description",
+            "add": "Add Rule Chain",
+            "set-root": "Make rule chain root",
+            "set-root-rulechain-title": "Are you sure you want to make the rule chain '{{ruleChainName}}' root?",
+            "set-root-rulechain-text": "After the confirmation the rule chain will become root and will handle all incoming transport messages.",
+            "delete-rulechain-title": "Are you sure you want to delete the rule chain '{{ruleChainName}}'?",
+            "delete-rulechain-text": "Be careful, after the confirmation the rule chain and all related data will become unrecoverable.",
+            "delete-rulechains-title": "Are you sure you want to delete { count, select, 1 {1 rule chain} other {# rule chains} }?",
+            "delete-rulechains-action-title": "Delete { count, select, 1 {1 rule chain} other {# rule chains} }",
+            "delete-rulechains-text": "Be careful, after the confirmation all selected rule chains will be removed and all related data will become unrecoverable.",
+            "add-rulechain-text": "Add new rule chain",
+            "no-rulechains-text": "No rule chains found",
+            "rulechain-details": "Rule chain details",
+            "details": "Details",
+            "events": "Events",
+            "system": "System",
+            "import": "Import rule chain",
+            "export": "Export rule chain",
+            "export-failed-error": "Unable to export rule chain: {{error}}",
+            "create-new-rulechain": "Create new rule chain",
+            "rulechain-file": "Rule chain file",
+            "invalid-rulechain-file-error": "Unable to import rule chain: Invalid rule chain data structure.",
+            "copyId": "Copy rule chain Id",
+            "idCopiedMessage": "Rule chain Id has been copied to clipboard",
+            "select-rulechain": "Select rule chain",
+            "no-rulechains-matching": "No rule chains matching '{{entity}}' were found.",
+            "rulechain-required": "Rule chain is required",
+            "management": "Rules management",
+            "debug-mode": "Debug mode"
+        },
+        "rulenode": { // TODO
+            "details": "Details",
+            "events": "Events",
+            "search": "Search nodes",
+            "open-node-library": "Open node library",
+            "add": "Add rule node",
+            "name": "Name",
+            "name-required": "Name is required.",
+            "type": "Type",
+            "description": "Description",
+            "delete": "Delete rule node",
+            "select-all-objects": "Select all nodes and connections",
+            "deselect-all-objects": "Deselect all nodes and connections",
+            "delete-selected-objects": "Delete selected nodes and connections",
+            "delete-selected": "Delete selected",
+            "select-all": "Select all",
+            "copy-selected": "Copy selected",
+            "deselect-all": "Deselect all",
+            "rulenode-details": "Rule node details",
+            "debug-mode": "Debug mode",
+            "configuration": "Configuration",
+            "link": "Link",
+            "link-details": "Rule node link details",
+            "add-link": "Add link",
+            "link-label": "Link label",
+            "link-label-required": "Link label is required.",
+            "custom-link-label": "Custom link label",
+            "custom-link-label-required": "Custom link label is required.",
+            "type-filter": "Filter",
+            "type-filter-details": "Filter incoming messages with configured conditions",
+            "type-enrichment": "Enrichment",
+            "type-enrichment-details": "Add additional information into Message Metadata",
+            "type-transformation": "Transformation",
+            "type-transformation-details": "Change Message payload and Metadata",
+            "type-action": "Action",
+            "type-action-details": "Perform special action",
+            "type-external": "External",
+            "type-external-details": "Interacts with external system",
+            "type-rule-chain": "Rule Chain",
+            "type-rule-chain-details": "Forwards incoming messages to specified Rule Chain",
+            "type-input": "Input",
+            "type-input-details": "Logical input of Rule Chain, forwards incoming messages to next related Rule Node",
+            "directive-is-not-loaded": "Defined configuration directive '{{directiveName}}' is not available.",
+            "ui-resources-load-error": "Failed to load configuration ui resources.",
+            "invalid-target-rulechain": "Unable to resolve target rule chain!",
+            "test-script-function": "Test script function",
+            "message": "Message",
+            "message-type": "Message type",
+            "message-type-required": "Message type is required",
+            "metadata": "Metadata",
+            "metadata-required": "Metadata entries can't be empty.",
+            "output": "Output",
+            "test": "Test",
+            "help": "Help"
         },
         "tenant": {
-              "tenants": "Tenants",
-              "management": "Gestión de Tenant",
-              "add": "Agregar Tenant",
-              "admins": "Admins",
-              "manage-tenant-admins": "Gestionar administradores tenant",
-              "delete": "Eliminar tenant",
-              "add-tenant-text": "Agregar nuevo tenant",
-              "no-tenants-text": "Ningún tenant encontrado",
-              "tenant-details": "Detalles del Tenant",
-              "delete-tenant-title": "¿Estás seguro que quieres eliminar el tenant '{{tenantTitle}}'?",
-              "delete-tenant-text": "Ten cuidado, luego de confirmar el tenant será eliminado y la información relacionada será irrecuperable.",
-              "delete-tenants-title": "¿Estás seguro que quieres eliminar { count, select, 1 {1 tenant} other {# tenants} }?",
-              "delete-tenants-action-title": "Eliminar { count, select, 1 {1 tenant} other {# tenants} }",
-              "delete-tenants-text": "Ten cuidado, luego de confirmar los tenants seleccionados serán eliminados y la información relacionada será irrecuperable.",
-              "title": "Título",
-              "title-required": "Título requerido.",
-              "description": "Descripción"
+            "tenants": "Tenants",
+            "management": "Gestión de Tenant",
+            "add": "Agregar Tenant",
+            "admins": "Admins",
+            "manage-tenant-admins": "Gestionar administradores tenant",
+            "delete": "Eliminar tenant",
+            "add-tenant-text": "Agregar nuevo tenant",
+            "no-tenants-text": "Ningún tenant encontrado",
+            "tenant-details": "Detalles del Tenant",
+            "delete-tenant-title": "¿Estás seguro que quieres eliminar el tenant '{{tenantTitle}}'?",
+            "delete-tenant-text": "Ten cuidado, luego de confirmar el tenant será eliminado y la información relacionada será irrecuperable.",
+            "delete-tenants-title": "¿Estás seguro que quieres eliminar { count, select, 1 {1 tenant} other {# tenants} }?",
+            "delete-tenants-action-title": "Eliminar { count, select, 1 {1 tenant} other {# tenants} }",
+            "delete-tenants-text": "Ten cuidado, luego de confirmar los tenants seleccionados serán eliminados y la información relacionada será irrecuperable.",
+            "title": "Título",
+            "title-required": "Título requerido.",
+            "description": "Descripción"
         },
         "timeinterval": {
-              "seconds-interval": "{ seconds, select, 1 {1 segundo} other {# segundos} }",
-              "minutes-interval": "{ minutes, select, 1 {1 minuto} other {# minutos} }",
-              "hours-interval": "{ hours, select, 1 {1 hora} other {# horas} }",
-              "days-interval": "{ days, select, 1 {1 día} other {# días} }",
-              "days": "Días",
-              "hours": "Horas",
-              "minutes": "Minutos",
-              "seconds": "Segundos",
-              "advanced": "Avanzado"
+            "seconds-interval": "{ seconds, select, 1 {1 segundo} other {# segundos} }",
+            "minutes-interval": "{ minutes, select, 1 {1 minuto} other {# minutos} }",
+            "hours-interval": "{ hours, select, 1 {1 hora} other {# horas} }",
+            "days-interval": "{ days, select, 1 {1 día} other {# días} }",
+            "days": "Días",
+            "hours": "Horas",
+            "minutes": "Minutos",
+            "seconds": "Segundos",
+            "advanced": "Avanzado"
         },
         "timewindow": {
-              "days": "{ days, select, 1 { día } other {# días } }",
-              "hours": "{ hours, select, 0 { horas } 1 {1 hora } other {# horas } }",
-              "minutes": "{ minutes, select, 0 { minutos } 1 {1 minuto } other {# minutos } }",
-              "seconds": "{ seconds, select, 0 { segundos } 1 {1 segundo } other {# segundos } }",
-              "realtime": "Tiempo-real",
-              "history": "Histórico",
-              "last-prefix": "último",
-              "period": "desde {{ startTime }} hasta {{ endTime }}",
-              "edit": "Editar ventana de tiempo",
-              "date-range": "Rango de fechas",
-              "last": "Últimos",
-              "time-period": "Período de tiempo"
+            "days": "{ days, select, 1 { día } other {# días } }",
+            "hours": "{ hours, select, 0 { horas } 1 {1 hora } other {# horas } }",
+            "minutes": "{ minutes, select, 0 { minutos } 1 {1 minuto } other {# minutos } }",
+            "seconds": "{ seconds, select, 0 { segundos } 1 {1 segundo } other {# segundos } }",
+            "realtime": "Tiempo-real",
+            "history": "Histórico",
+            "last-prefix": "último",
+            "period": "desde {{ startTime }} hasta {{ endTime }}",
+            "edit": "Editar ventana de tiempo",
+            "date-range": "Rango de fechas",
+            "last": "Últimos",
+            "time-period": "Período de tiempo"
         },
         "user": {
-              "users": "Usuarios",
-              "customer-users": "Usuarios del Cliente",
-              "tenant-admins": "Tenant Admins",
-              "sys-admin": "Administrador del Sistema",
-              "tenant-admin": "Administrador Tenant",
-              "customer": "Cliente",
-              "anonymous": "Anónimo",
-              "add": "Agregar usuario",
-              "delete": "Eliminar usuario",
-              "add-user-text": "Agregar nuevo usuario",
-              "no-users-text": "Ningún usuario encontrado",
-              "user-details": "Detalles del usuario",
-              "delete-user-title": "¿Estás seguro que quieres eliminar el usuario '{{userEmail}}'?",
-              "delete-user-text": "Ten cuidado, luego de confirmar el usuario seleccionado será eliminado y la información relacionada será irrecuperable.",
-              "delete-users-title": "¿Estás seguro que quieres eliminar { count, select, 1 {1 usuario} other {# usuarios} }?",
-              "delete-users-action-title": "Borrar { count, select, 1 {1 usuario} other {# usuarios} }",
-              "delete-users-text": "Ten cuidado, luego de confirmar los usuarios seleccionados serán eliminados y la información relacionada será irrecuperable.",
-              "activation-email-sent-message": "Mail de activación enviado con éxito!",
-              "resend-activation": "Reenviar activación",
-              "email": "Email",
-              "email-required": "Email requerido.",
-              "first-name": "Nombre",
-              "last-name": "Apellido",
-              "description": "Descripción",
-              "default-dashboard": "Panel por defecto",
-              "always-fullscreen": "Siempre en pantalla completa"
+            "users": "Usuarios",
+            "customer-users": "Usuarios del Cliente",
+            "tenant-admins": "Tenant Admins",
+            "sys-admin": "Administrador del Sistema",
+            "tenant-admin": "Administrador Tenant",
+            "customer": "Cliente",
+            "anonymous": "Anónimo",
+            "add": "Agregar usuario",
+            "delete": "Eliminar usuario",
+            "add-user-text": "Agregar nuevo usuario",
+            "no-users-text": "Ningún usuario encontrado",
+            "user-details": "Detalles del usuario",
+            "delete-user-title": "¿Estás seguro que quieres eliminar el usuario '{{userEmail}}'?",
+            "delete-user-text": "Ten cuidado, luego de confirmar el usuario seleccionado será eliminado y la información relacionada será irrecuperable.",
+            "delete-users-title": "¿Estás seguro que quieres eliminar { count, select, 1 {1 usuario} other {# usuarios} }?",
+            "delete-users-action-title": "Borrar { count, select, 1 {1 usuario} other {# usuarios} }",
+            "delete-users-text": "Ten cuidado, luego de confirmar los usuarios seleccionados serán eliminados y la información relacionada será irrecuperable.",
+            "activation-email-sent-message": "Mail de activación enviado con éxito!",
+            "resend-activation": "Reenviar activación",
+            "email": "Email",
+            "email-required": "Email requerido.",
+            "first-name": "Nombre",
+            "last-name": "Apellido",
+            "description": "Descripción",
+            "default-dashboard": "Panel por defecto",
+            "always-fullscreen": "Siempre en pantalla completa"
         },
         "value": {
-              "type": "Tipo de valor",
-              "string": "Cadena de texto",
-              "string-value": "Valor de cadena de texto",
-              "integer": "Nro entero",
-              "integer-value": "Valor de nro entero",
-              "invalid-integer-value": "Valor inválido",
-              "double": "Nro decimal",
-              "double-value": "Valor nro decimal",
-              "boolean": "Booleano",
-              "boolean-value": "Valor booleano",
-              "false": "Falso",
-              "true": "Verdadero"
+            "type": "Tipo de valor",
+            "string": "Cadena de texto",
+            "string-value": "Valor de cadena de texto",
+            "integer": "Nro entero",
+            "integer-value": "Valor de nro entero",
+            "invalid-integer-value": "Valor inválido",
+            "double": "Nro decimal",
+            "double-value": "Valor nro decimal",
+            "boolean": "Booleano",
+            "boolean-value": "Valor booleano",
+            "false": "Falso",
+            "true": "Verdadero"
         },
         "widget": {
-              "widget-library": "Bibloteca de Widgets",
-              "widget-bundle": "Paquetes de Widgets",
-              "select-widgets-bundle": "Seleccionar paquete de widgets",
-              "management": "Gestión de Widgets",
-              "editor": "Editor de widgets",
-              "widget-type-not-found": "Problema al cargar la configuración del widget.<br>Probablemente asociado\n    El tipo de widget fue eliminado.",
-              "widget-type-load-error": "Widget no pudo ser cargado debido a estos errores:",
-              "remove": "Eliminar widget",
-              "edit": "Editar widget",
-              "remove-widget-title": "¿Estás seguro que quieres eliminar el widget '{{widgetTitle}}'?",
-              "remove-widget-text": "Luego de confirmar el widget será eliminado y toda la información relacionada será irrecuperable..",
-              "timeseries": "Series de tiempo",
-              "latest-values": "Últimos valores",
-              "rpc": "Widget de control",
-              "static": "Widget estático",
-              "select-widget-type": "Seleccionar tipo de widget",
-              "missing-widget-title-error": "El titulo del widget debe ser especificado!",
-              "widget-saved": "Widget guardado",
-              "unable-to-save-widget-error": "Imposible guardar widget! Tiene errores!",
-              "save": "Guardar widget",
-              "saveAs": "Guardar widget como",
-              "save-widget-type-as": "Guardar tipo de widget como",
-              "save-widget-type-as-text": "Por favor, ingrese un nuevo titulo y/o seleccione un paquete de destino.",
-              "toggle-fullscreen": "Cambiar a pantalla completa",
-              "run": "Correr widget",
-              "title": "Titulo",
-              "title-required": "Titulo requerido.",
-              "type": "Tipo",
-              "resources": "Recursos",
-              "resource-url": "JavaScript/CSS URL",
-              "remove-resource": "Eliminar recurso",
-              "add-resource": "Agregar recurso",
-              "html": "HTML",
-              "tidy": "Tidy",
-              "css": "CSS",
-              "settings-schema": "Esquema de configuración",
-              "datakey-settings-schema": "Esquema de configuración de clave de datos",
-              "javascript": "Javascript",
-              "remove-widget-type-title": "¿Estás seguro que quieres eliminar el tipo del widget '{{widgetName}}'?",
-              "remove-widget-type-text": "Luego de confirmar el tipo será eliminado y la información relacionada será irrecuperable.",
-              "remove-widget-type": "Eliminar tipo de widget.",
-              "add-widget-type": "Agregar nuevo tipo de widget",
-              "widget-type-load-failed-error": "Error al cargar el tipo de widget!",
-              "widget-template-load-failed-error": "Error al cargar el template del widget!",
-              "add": "Agregar Widget",
-              "undo": "Deshacer cambios",
-              "export": "Exportar widget"
+            "widget-library": "Bibloteca de Widgets",
+            "widget-bundle": "Paquetes de Widgets",
+            "select-widgets-bundle": "Seleccionar paquete de widgets",
+            "management": "Gestión de Widgets",
+            "editor": "Editor de widgets",
+            "widget-type-not-found": "Problema al cargar la configuración del widget.<br>Probablemente asociado\n    El tipo de widget fue eliminado.",
+            "widget-type-load-error": "Widget no pudo ser cargado debido a estos errores:",
+            "remove": "Eliminar widget",
+            "edit": "Editar widget",
+            "remove-widget-title": "¿Estás seguro que quieres eliminar el widget '{{widgetTitle}}'?",
+            "remove-widget-text": "Luego de confirmar el widget será eliminado y toda la información relacionada será irrecuperable..",
+            "timeseries": "Series de tiempo",
+            "latest-values": "Últimos valores",
+            "rpc": "Widget de control",
+            "static": "Widget estático",
+            "select-widget-type": "Seleccionar tipo de widget",
+            "missing-widget-title-error": "El titulo del widget debe ser especificado!",
+            "widget-saved": "Widget guardado",
+            "unable-to-save-widget-error": "Imposible guardar widget! Tiene errores!",
+            "save": "Guardar widget",
+            "saveAs": "Guardar widget como",
+            "save-widget-type-as": "Guardar tipo de widget como",
+            "save-widget-type-as-text": "Por favor, ingrese un nuevo titulo y/o seleccione un paquete de destino.",
+            "toggle-fullscreen": "Cambiar a pantalla completa",
+            "run": "Correr widget",
+            "title": "Titulo",
+            "title-required": "Titulo requerido.",
+            "type": "Tipo",
+            "resources": "Recursos",
+            "resource-url": "JavaScript/CSS URL",
+            "remove-resource": "Eliminar recurso",
+            "add-resource": "Agregar recurso",
+            "html": "HTML",
+            "tidy": "Tidy",
+            "css": "CSS",
+            "settings-schema": "Esquema de configuración",
+            "datakey-settings-schema": "Esquema de configuración de clave de datos",
+            "javascript": "Javascript",
+            "remove-widget-type-title": "¿Estás seguro que quieres eliminar el tipo del widget '{{widgetName}}'?",
+            "remove-widget-type-text": "Luego de confirmar el tipo será eliminado y la información relacionada será irrecuperable.",
+            "remove-widget-type": "Eliminar tipo de widget.",
+            "add-widget-type": "Agregar nuevo tipo de widget",
+            "widget-type-load-failed-error": "Error al cargar el tipo de widget!",
+            "widget-template-load-failed-error": "Error al cargar el template del widget!",
+            "add": "Agregar Widget",
+            "undo": "Deshacer cambios",
+            "export": "Exportar widget"
+        },
+        "widget-action": { // TODO
+            "header-button": "Widget header button",
+            "open-dashboard-state": "Navigate to new dashboard state",
+            "update-dashboard-state": "Update current dashboard state",
+            "open-dashboard": "Navigate to other dashboard",
+            "custom": "Custom action",
+            "target-dashboard-state": "Target dashboard state",
+            "target-dashboard-state-required": "Target dashboard state is required",
+            "set-entity-from-widget": "Set entity from widget",
+            "target-dashboard": "Target dashboard",
+            "open-right-layout": "Open right dashboard layout (mobile view)"
         },
         "widgets-bundle": {
-              "current": "Paquete actual",
-              "widgets-bundles": "Paquete de Widgets",
-              "add": "Agregar paquete de widgets",
-              "delete": "Eliminar paquete de widgets",
-              "title": "Título",
-              "title-required": "Título requerido.",
-              "add-widgets-bundle-text": "Agregar nuevo paquete de widgets",
-              "no-widgets-bundles-text": "Ningún paquete de widgets encontrado",
-              "empty": "Paquete de widgets vacío.",
-              "details": "Detalles",
-              "widgets-bundle-details": "Detalles del paquete de Widgets",
-              "delete-widgets-bundle-title": "¿Estás seguro que  desea eliminar el paquete de widgets '{{widgetsBundleTitle}}'?",
-              "delete-widgets-bundle-text": "Ten cuidado, luego de confirmar todos los paquetes seleccionados serán eliminados y su información relacionada será irrecuperable.",
-              "delete-widgets-bundles-title": "¿Estás seguro que deseas eliminar { count, select, 1 {1 paquete de widgets} other {# paquetes de widgets} }?",
-              "delete-widgets-bundles-action-title": "Eliminar { count, select, 1 {1 paquete de widgets} other {# paquetes de widgets} }",
-              "delete-widgets-bundles-text": "Ten cuidado, luego de confirmar todos los paquetes seleccionados serán eliminados y la información relacionada será irrecuperable.",
-              "no-widgets-bundles-matching": "Ningún paquete '{{widgetsBundle}}' encontrado.",
-              "widgets-bundle-required": "Paquete de widget requerido.",
-              "system": "Sistema",
-              "import": "Importar paquete de widgets",
-              "export": "Exportar paquete de widgets",
-              "export-failed-error": "Imposible exportar paquete de widgets: {{error}}",
-              "create-new-widgets-bundle": "Crear nuevo paquete de widgets",
-              "widgets-bundle-file": "Archivo de paquete de widgets",
-              "invalid-widgets-bundle-file-error": "Imposible importar paquete de widgets: Estructura de datos inválida."
+            "current": "Paquete actual",
+            "widgets-bundles": "Paquete de Widgets",
+            "add": "Agregar paquete de widgets",
+            "delete": "Eliminar paquete de widgets",
+            "title": "Título",
+            "title-required": "Título requerido.",
+            "add-widgets-bundle-text": "Agregar nuevo paquete de widgets",
+            "no-widgets-bundles-text": "Ningún paquete de widgets encontrado",
+            "empty": "Paquete de widgets vacío.",
+            "details": "Detalles",
+            "widgets-bundle-details": "Detalles del paquete de Widgets",
+            "delete-widgets-bundle-title": "¿Estás seguro que  desea eliminar el paquete de widgets '{{widgetsBundleTitle}}'?",
+            "delete-widgets-bundle-text": "Ten cuidado, luego de confirmar todos los paquetes seleccionados serán eliminados y su información relacionada será irrecuperable.",
+            "delete-widgets-bundles-title": "¿Estás seguro que deseas eliminar { count, select, 1 {1 paquete de widgets} other {# paquetes de widgets} }?",
+            "delete-widgets-bundles-action-title": "Eliminar { count, select, 1 {1 paquete de widgets} other {# paquetes de widgets} }",
+            "delete-widgets-bundles-text": "Ten cuidado, luego de confirmar todos los paquetes seleccionados serán eliminados y la información relacionada será irrecuperable.",
+            "no-widgets-bundles-matching": "Ningún paquete '{{widgetsBundle}}' encontrado.",
+            "widgets-bundle-required": "Paquete de widget requerido.",
+            "system": "Sistema",
+            "import": "Importar paquete de widgets",
+            "export": "Exportar paquete de widgets",
+            "export-failed-error": "Imposible exportar paquete de widgets: {{error}}",
+            "create-new-widgets-bundle": "Crear nuevo paquete de widgets",
+            "widgets-bundle-file": "Archivo de paquete de widgets",
+            "invalid-widgets-bundle-file-error": "Imposible importar paquete de widgets: Estructura de datos inválida."
         },
         "widget-config": {
-              "data": "Datos",
-              "settings": "Ajustes",
-              "advanced": "Avanzado",
-              "title": "Titulo",
-              "general-settings": "Ajustes generales",
-              "display-title": "Mostrar titulo",
-              "drop-shadow": "Sombra",
-              "enable-fullscreen": "Habilitar pantalla completa",
-              "background-color": "Color de fondo",
-              "text-color": "Color del texto",
-              "padding": "Relleno",
-              "title-style": "Estilo de título",
-              "mobile-mode-settings": "Ajustes mobile.",
-              "order": "Orden",
-              "height": "Altura",
-              "units": "Caracter especial a mostrar en el siguiente valor",
-              "decimals": "Números de dígitos después de la coma",
-              "timewindow": "Ventana de tiempo",
-              "use-dashboard-timewindow": "Usar ventana de tiempo del Panel",
-              "display-legend": "Mostrar leyenda",
-              "datasources": "Set de datos",
-              "datasource-type": "Tipo",
-              "datasource-parameters": "Parámetros",
-              "remove-datasource": "Eliminar set de datos",
-              "add-datasource": "Agregar set de datos",
-              "target-device": "Dispositivo destino"
+            "data": "Datos",
+            "settings": "Ajustes",
+            "advanced": "Avanzado",
+            "title": "Titulo",
+            "general-settings": "Ajustes generales",
+            "display-title": "Mostrar titulo",
+            "drop-shadow": "Sombra",
+            "enable-fullscreen": "Habilitar pantalla completa",
+            "background-color": "Color de fondo",
+            "text-color": "Color del texto",
+            "padding": "Relleno",
+            "title-style": "Estilo de título",
+            "mobile-mode-settings": "Ajustes mobile.",
+            "order": "Orden",
+            "height": "Altura",
+            "units": "Caracter especial a mostrar en el siguiente valor",
+            "decimals": "Números de dígitos después de la coma",
+            "timewindow": "Ventana de tiempo",
+            "use-dashboard-timewindow": "Usar ventana de tiempo del Panel",
+            "display-legend": "Mostrar leyenda",
+            "datasources": "Set de datos",
+            "datasource-type": "Tipo",
+            "datasource-parameters": "Parámetros",
+            "remove-datasource": "Eliminar set de datos",
+            "add-datasource": "Agregar set de datos",
+            "target-device": "Dispositivo destino"
         },
         "widget-type": {
-              "import": "Importar tipo de widget",
-              "export": "Exportar tipo de widget",
-              "export-failed-error": "Imposible exportar tipo de widget: {{error}}",
-              "create-new-widget-type": "Crear nuevo tipo de widget",
-              "widget-type-file": "Tipo de archivo del widget",
-              "invalid-widget-type-file-error": "Imposible de importar tipo de widget: Estructura de datos inválida."
+            "import": "Importar tipo de widget",
+            "export": "Exportar tipo de widget",
+            "export-failed-error": "Imposible exportar tipo de widget: {{error}}",
+            "create-new-widget-type": "Crear nuevo tipo de widget",
+            "widget-type-file": "Tipo de archivo del widget",
+            "invalid-widget-type-file-error": "Imposible de importar tipo de widget: Estructura de datos inválida."
+        },
+        "icon": { // TODO
+            "icon": "Icon",
+            "select-icon": "Select icon",
+            "material-icons": "Material icons",
+            "show-all": "Show all icons"
+        },
+        "custom": { // TODO
+            "widget-action": {
+                "action-cell-button": "Action cell button",
+                "row-click": "On row click",
+                "marker-click": "On marker click",
+                "tooltip-tag-action": "Tooltip tag action"
+            }
         },
         "language": {
-              "language": "Lenguaje",
-              "en_US": "Inglés",
-              "ko_KR": "Coreano",
-              "zh_CN": "Chino",
-              "ru_RU": "Ruso",
-              "es_ES": "Español"
+            "language": "Lenguaje",
+            "en_US": "Inglés",
+            "ko_KR": "Coreano",
+            "zh_CN": "Chino",
+            "ru_RU": "Ruso",
+            "es_ES": "Español"
         }
-  };
-  angular.extend(locales, {'es_ES': es_ES});
+    };
+    angular.extend(locales, { 'es_ES': es_ES });
 }
\ No newline at end of file
diff --git a/ui/src/app/locale/locale.constant-ko.js b/ui/src/app/locale/locale.constant-ko.js
index 9273037..9112157 100644
--- a/ui/src/app/locale/locale.constant-ko.js
+++ b/ui/src/app/locale/locale.constant-ko.js
@@ -38,8 +38,11 @@ export default function addLocaleKorean(locales) {
             "update": "업데이트",
             "remove": "제거",
             "search": "검색",
+            "clear-search": "Clear search", // TODO
             "assign": "할당",
             "unassign": "비할당",
+            "share": "Share", // TODO
+            "make-private": "Make private", // TODO
             "apply": "적용",
             "apply-changes": "변경사항 적용",
             "edit-mode": "수정 모드",
@@ -57,8 +60,11 @@ export default function addLocaleKorean(locales) {
             "undo": "취소",
             "copy": "복사",
             "paste": "붙여넣기",
+            "copy-reference": "Copy reference", // TODO
+            "paste-reference": "Paste reference", // TODO
             "import": "가져오기",
-            "export": "내보내기"
+            "export": "내보내기",
+            "share-via": "Share via {{provider}}" // TODO
         },
         "aggregation": {
             "aggregation": "집합",
@@ -95,6 +101,160 @@ export default function addLocaleKorean(locales) {
             "enable-tls": "TLS 사용",
             "send-test-mail": "테스트 메일 보내기"
         },
+
+        "alarm": { // TODO
+            "alarm": "Alarm",
+            "alarms": "Alarms",
+            "select-alarm": "Select alarm",
+            "no-alarms-matching": "No alarms matching '{{entity}}' were found.",
+            "alarm-required": "Alarm is required",
+            "alarm-status": "Alarm status",
+            "search-status": {
+                "ANY": "Any",
+                "ACTIVE": "Active",
+                "CLEARED": "Cleared",
+                "ACK": "Acknowledged",
+                "UNACK": "Unacknowledged"
+            },
+            "display-status": {
+                "ACTIVE_UNACK": "Active Unacknowledged",
+                "ACTIVE_ACK": "Active Acknowledged",
+                "CLEARED_UNACK": "Cleared Unacknowledged",
+                "CLEARED_ACK": "Cleared Acknowledged"
+            },
+            "no-alarms-prompt": "No alarms found",
+            "created-time": "Created time",
+            "type": "Type",
+            "severity": "Severity",
+            "originator": "Originator",
+            "originator-type": "Originator type",
+            "details": "Details",
+            "status": "Status",
+            "alarm-details": "Alarm details",
+            "start-time": "Start time",
+            "end-time": "End time",
+            "ack-time": "Acknowledged time",
+            "clear-time": "Cleared time",
+            "severity-critical": "Critical",
+            "severity-major": "Major",
+            "severity-minor": "Minor",
+            "severity-warning": "Warning",
+            "severity-indeterminate": "Indeterminate",
+            "acknowledge": "Acknowledge",
+            "clear": "Clear",
+            "search": "Search alarms",
+            "selected-alarms": "{ count, select, 1 {1 alarm} other {# alarms} } selected",
+            "no-data": "No data to display",
+            "polling-interval": "Alarms polling interval (sec)",
+            "polling-interval-required": "Alarms polling interval is required.",
+            "min-polling-interval-message": "At least 1 sec polling interval is allowed.",
+            "aknowledge-alarms-title": "Acknowledge { count, select, 1 {1 alarm} other {# alarms} }",
+            "aknowledge-alarms-text": "Are you sure you want to acknowledge { count, select, 1 {1 alarm} other {# alarms} }?",
+            "clear-alarms-title": "Clear { count, select, 1 {1 alarm} other {# alarms} }",
+            "clear-alarms-text": "Are you sure you want to clear { count, select, 1 {1 alarm} other {# alarms} }?"
+        },
+        "alias": { // TODO
+            "add": "Add alias",
+            "edit": "Edit alias",
+            "name": "Alias name",
+            "name-required": "Alias name is required",
+            "duplicate-alias": "Alias with same name is already exists.",
+            "filter-type-single-entity": "Single entity",
+            "filter-type-entity-list": "Entity list",
+            "filter-type-entity-name": "Entity name",
+            "filter-type-state-entity": "Entity from dashboard state",
+            "filter-type-state-entity-description": "Entity taken from dashboard state parameters",
+            "filter-type-asset-type": "Asset type",
+            "filter-type-asset-type-description": "Assets of type '{{assetType}}'",
+            "filter-type-asset-type-and-name-description": "Assets of type '{{assetType}}' and with name starting with '{{prefix}}'",
+            "filter-type-device-type": "Device type",
+            "filter-type-device-type-description": "Devices of type '{{deviceType}}'",
+            "filter-type-device-type-and-name-description": "Devices of type '{{deviceType}}' and with name starting with '{{prefix}}'",
+            "filter-type-relations-query": "Relations query",
+            "filter-type-relations-query-description": "{{entities}} that have {{relationType}} relation {{direction}} {{rootEntity}}",
+            "filter-type-asset-search-query": "Asset search query",
+            "filter-type-asset-search-query-description": "Assets with types {{assetTypes}} that have {{relationType}} relation {{direction}} {{rootEntity}}",
+            "filter-type-device-search-query": "Device search query",
+            "filter-type-device-search-query-description": "Devices with types {{deviceTypes}} that have {{relationType}} relation {{direction}} {{rootEntity}}",
+            "entity-filter": "Entity filter",
+            "resolve-multiple": "Resolve as multiple entities",
+            "filter-type": "Filter type",
+            "filter-type-required": "Filter type is required.",
+            "entity-filter-no-entity-matched": "No entities matching specified filter were found.",
+            "no-entity-filter-specified": "No entity filter specified",
+            "root-state-entity": "Use dashboard state entity as root",
+            "root-entity": "Root entity",
+            "state-entity-parameter-name": "State entity parameter name",
+            "default-state-entity": "Default state entity",
+            "default-entity-parameter-name": "By default",
+            "max-relation-level": "Max relation level",
+            "unlimited-level": "Unlimited level",
+            "state-entity": "Dashboard state entity",
+            "all-entities": "All entities",
+            "any-relation": "any"
+        },
+        "asset": { // TODO
+            "asset": "Asset",
+            "assets": "Assets",
+            "management": "Asset management",
+            "view-assets": "View Assets",
+            "add": "Add Asset",
+            "assign-to-customer": "Assign to customer",
+            "assign-asset-to-customer": "Assign Asset(s) To Customer",
+            "assign-asset-to-customer-text": "Please select the assets to assign to the customer",
+            "no-assets-text": "No assets found",
+            "assign-to-customer-text": "Please select the customer to assign the asset(s)",
+            "public": "Public",
+            "assignedToCustomer": "Assigned to customer",
+            "make-public": "Make asset public",
+            "make-private": "Make asset private",
+            "unassign-from-customer": "Unassign from customer",
+            "delete": "Delete asset",
+            "asset-public": "Asset is public",
+            "asset-type": "Asset type",
+            "asset-type-required": "Asset type is required.",
+            "select-asset-type": "Select asset type",
+            "enter-asset-type": "Enter asset type",
+            "any-asset": "Any asset",
+            "no-asset-types-matching": "No asset types matching '{{entitySubtype}}' were found.",
+            "asset-type-list-empty": "No asset types selected.",
+            "asset-types": "Asset types",
+            "name": "Name",
+            "name-required": "Name is required.",
+            "description": "Description",
+            "type": "Type",
+            "type-required": "Type is required.",
+            "details": "Details",
+            "events": "Events",
+            "add-asset-text": "Add new asset",
+            "asset-details": "Asset details",
+            "assign-assets": "Assign assets",
+            "assign-assets-text": "Assign { count, select, 1 {1 asset} other {# assets} } to customer",
+            "delete-assets": "Delete assets",
+            "unassign-assets": "Unassign assets",
+            "unassign-assets-action-title": "Unassign { count, select, 1 {1 asset} other {# assets} } from customer",
+            "assign-new-asset": "Assign new asset",
+            "delete-asset-title": "Are you sure you want to delete the asset '{{assetName}}'?",
+            "delete-asset-text": "Be careful, after the confirmation the asset and all related data will become unrecoverable.",
+            "delete-assets-title": "Are you sure you want to delete { count, select, 1 {1 asset} other {# assets} }?",
+            "delete-assets-action-title": "Delete { count, select, 1 {1 asset} other {# assets} }",
+            "delete-assets-text": "Be careful, after the confirmation all selected assets will be removed and all related data will become unrecoverable.",
+            "make-public-asset-title": "Are you sure you want to make the asset '{{assetName}}' public?",
+            "make-public-asset-text": "After the confirmation the asset and all its data will be made public and accessible by others.",
+            "make-private-asset-title": "Are you sure you want to make the asset '{{assetName}}' private?",
+            "make-private-asset-text": "After the confirmation the asset and all its data will be made private and won't be accessible by others.",
+            "unassign-asset-title": "Are you sure you want to unassign the asset '{{assetName}}'?",
+            "unassign-asset-text": "After the confirmation the asset will be unassigned and won't be accessible by the customer.",
+            "unassign-asset": "Unassign asset",
+            "unassign-assets-title": "Are you sure you want to unassign { count, select, 1 {1 asset} other {# assets} }?",
+            "unassign-assets-text": "After the confirmation all selected assets will be unassigned and won't be accessible by the customer.",
+            "copyId": "Copy asset Id",
+            "idCopiedMessage": "Asset Id has been copied to clipboard",
+            "select-asset": "Select asset",
+            "no-assets-matching": "No assets matching '{{entity}}' were found.",
+            "asset-required": "Asset is required",
+            "name-starts-with": "Asset name starts with"
+        },
         "attribute": {
             "attributes": "속성",
             "latest-telemetry": "최근 데이터",
@@ -104,9 +264,9 @@ export default function addLocaleKorean(locales) {
             "scope-server": "서버 속성",
             "scope-shared": "공유 속성",
             "add": "속성 추가",
-            "key": "Key",
+            "key": "Key", // TODO
             "key-required": "속성 key를 입력하세요.",
-            "value": "Value",
+            "value": "Value", // TODO
             "value-required": "속성 value를 입력하세요.",
             "delete-attributes-title": "{ count, select, 1 {속성} other {여러 속성들을} } 삭제하시겠습니까??",
             "delete-attributes-text": "모든 선택된 속성들이 제거 될 것이므로 주의하십시오.",
@@ -121,6 +281,38 @@ export default function addLocaleKorean(locales) {
             "selected-attributes": "{ count, select, 1 {속성 1개} other {속성 #개} } 선택됨",
             "selected-telemetry": "{ count, select, 1 {최근 데이터 1개} other {최근 데이터 #개} } 선택됨"
         },
+        "audit-log": { // TODO
+            "audit": "Audit",
+            "audit-logs": "Audit Logs",
+            "timestamp": "Timestamp",
+            "entity-type": "Entity Type",
+            "entity-name": "Entity Name",
+            "user": "User",
+            "type": "Type",
+            "status": "Status",
+            "details": "Details",
+            "type-added": "Added",
+            "type-deleted": "Deleted",
+            "type-updated": "Updated",
+            "type-attributes-updated": "Attributes updated",
+            "type-attributes-deleted": "Attributes deleted",
+            "type-rpc-call": "RPC call",
+            "type-credentials-updated": "Credentials updated",
+            "type-assigned-to-customer": "Assigned to Customer",
+            "type-unassigned-from-customer": "Unassigned from Customer",
+            "type-activated": "Activated",
+            "type-suspended": "Suspended",
+            "type-credentials-read": "Credentials read",
+            "type-attributes-read": "Attributes read",
+            "status-success": "Success",
+            "status-failure": "Failure",
+            "audit-log-details": "Audit log details",
+            "no-audit-logs-prompt": "No logs found",
+            "action-data": "Action data",
+            "failure-details": "Failure details",
+            "search": "Search audit logs",
+            "clear-search": "Clear search"
+        },
         "confirm-on-exit": {
             "message": "변경 사항을 저장하지 않았습니다. 이 페이지를 나가시겠습니까?",
             "html-message": "변경 사항을 저장하지 않았습니다.<br/>이 페이지를 나가시겠습니까?",
@@ -145,6 +337,11 @@ export default function addLocaleKorean(locales) {
             "enter-password": "비밀번호를 입력하세요.",
             "enter-search": "검색어 입력"
         },
+        "content-type": { // TODO
+            "json": "Json",
+            "text": "Text",
+            "binary": "Binary (Base64)"
+        },
         "customer": {
             "customers": "커스터머",
             "management": "커스터머 관리",
@@ -156,6 +353,10 @@ export default function addLocaleKorean(locales) {
             "manage-customer-users": "커스터머 사용자 관리",
             "manage-customer-devices": "커스터머 디바이스 관리",
             "manage-customer-dashboards": "커스터머 대시보드 관리",
+            "manage-public-devices": "Manage public devices", // TODO
+            "manage-public-dashboards": "Manage public dashboards", // TODO
+            "manage-customer-assets": "Manage customer assets", // TODO
+            "manage-public-assets": "Manage public assets", // TODO
             "add-customer-text": "커스터머 추가",
             "no-customers-text": "커스터머가 없습니다.",
             "customer-details": "커스터머 상세정보",
@@ -169,7 +370,17 @@ export default function addLocaleKorean(locales) {
             "manage-dashboards": "대시보드 관리",
             "title": "타이틀",
             "title-required": "타이틀을 입력하세요.",
-            "description": "설명"
+            "description": "설명",
+            "details": "Details",
+            "events": "Events",
+            "copyId": "Copy customer Id",
+            "idCopiedMessage": "Customer Id has been copied to clipboard",
+            "select-customer": "Select customer",
+            "no-customers-matching": "No customers matching '{{entity}}' were found.",
+            "customer-required": "Customer is required",
+            "select-default-customer": "Select default customer",
+            "default-customer": "Default customer",
+            "default-customer-required": "Default customer is required in order to debug dashboard on Tenant level"
         },
         "datetime": {
             "date-from": "시작 날짜",
@@ -277,11 +488,15 @@ export default function addLocaleKorean(locales) {
             "attributes": "Attributes",
             "timeseries-required": "디바이스 timeseries 를 입력하세요.",
             "timeseries-or-attributes-required": "디바이스 timeseries/attributes 를 입력하세요.",
+            "maximum-timeseries-or-attributes": "Maximum { count, select, 1 {1 timeseries/attribute is allowed.} other {# timeseries/attributes are allowed} }", // TODO
+            "alarm-fields-required": "Alarm fields are required.", // TODO
             "function-types": "함수 유형",
-            "function-types-required": "함수 유형을 입력하세요."
+            "function-types-required": "함수 유형을 입력하세요.",
+            "maximum-function-types": "Maximum { count, select, 1 {1 function type is allowed.} other {# function types are allowed} }" // TODO
         },
         "datasource": {
             "type": "데이터소스 유형",
+            "name": "Name", // TODO
             "add-datasource-prompt": "데이터소스를 추가하세요."
         },
         "details": {
@@ -375,11 +590,101 @@ export default function addLocaleKorean(locales) {
             "unhandled-error-code": "처리되지 않은 오류 코드: {{errorCode}}",
             "unknown-error": "알 수 없는 오류"
         },
+        "entity": { // TODO
+            "entity": "Entity",
+            "entities": "Entities",
+            "aliases": "Entity aliases",
+            "entity-alias": "Entity alias",
+            "unable-delete-entity-alias-title": "Unable to delete entity alias",
+            "unable-delete-entity-alias-text": "Entity alias '{{entityAlias}}' can't be deleted as it used by the following widget(s):<br/>{{widgetsList}}",
+            "duplicate-alias-error": "Duplicate alias found '{{alias}}'.<br>Entity aliases must be unique whithin the dashboard.",
+            "missing-entity-filter-error": "Filter is missing for alias '{{alias}}'.",
+            "configure-alias": "Configure '{{alias}}' alias",
+            "alias": "Alias",
+            "alias-required": "Entity alias is required.",
+            "remove-alias": "Remove entity alias",
+            "add-alias": "Add entity alias",
+            "entity-list": "Entity list",
+            "entity-type": "Entity type",
+            "entity-types": "Entity types",
+            "entity-type-list": "Entity type list",
+            "any-entity": "Any entity",
+            "enter-entity-type": "Enter entity type",
+            "no-entities-matching": "No entities matching '{{entity}}' were found.",
+            "no-entity-types-matching": "No entity types matching '{{entityType}}' were found.",
+            "name-starts-with": "Name starts with",
+            "use-entity-name-filter": "Use filter",
+            "entity-list-empty": "No entities selected.",
+            "entity-type-list-empty": "No entity types selected.",
+            "entity-name-filter-required": "Entity name filter is required.",
+            "entity-name-filter-no-entity-matched": "No entities starting with '{{entity}}' were found.",
+            "all-subtypes": "All",
+            "select-entities": "Select entities",
+            "no-aliases-found": "No aliases found.",
+            "no-alias-matching": "'{{alias}}' not found.",
+            "create-new-alias": "Create a new one!",
+            "key": "Key",
+            "key-name": "Key name",
+            "no-keys-found": "No keys found.",
+            "no-key-matching": "'{{key}}' not found.",
+            "create-new-key": "Create a new one!",
+            "type": "Type",
+            "type-required": "Entity type is required.",
+            "type-device": "Device",
+            "type-devices": "Devices",
+            "list-of-devices": "{ count, select, 1 {One device} other {List of # devices} }",
+            "device-name-starts-with": "Devices whose names start with '{{prefix}}'",
+            "type-asset": "Asset",
+            "type-assets": "Assets",
+            "list-of-assets": "{ count, select, 1 {One asset} other {List of # assets} }",
+            "asset-name-starts-with": "Assets whose names start with '{{prefix}}'",
+            "type-rule": "Rule",
+            "type-rules": "Rules",
+            "list-of-rules": "{ count, select, 1 {One rule} other {List of # rules} }",
+            "rule-name-starts-with": "Rules whose names start with '{{prefix}}'",
+            "type-plugin": "Plugin",
+            "type-plugins": "Plugins",
+            "list-of-plugins": "{ count, select, 1 {One plugin} other {List of # plugins} }",
+            "plugin-name-starts-with": "Plugins whose names start with '{{prefix}}'",
+            "type-tenant": "Tenant",
+            "type-tenants": "Tenants",
+            "list-of-tenants": "{ count, select, 1 {One tenant} other {List of # tenants} }",
+            "tenant-name-starts-with": "Tenants whose names start with '{{prefix}}'",
+            "type-customer": "Customer",
+            "type-customers": "Customers",
+            "list-of-customers": "{ count, select, 1 {One customer} other {List of # customers} }",
+            "customer-name-starts-with": "Customers whose names start with '{{prefix}}'",
+            "type-user": "User",
+            "type-users": "Users",
+            "list-of-users": "{ count, select, 1 {One user} other {List of # users} }",
+            "user-name-starts-with": "Users whose names start with '{{prefix}}'",
+            "type-dashboard": "Dashboard",
+            "type-dashboards": "Dashboards",
+            "list-of-dashboards": "{ count, select, 1 {One dashboard} other {List of # dashboards} }",
+            "dashboard-name-starts-with": "Dashboards whose names start with '{{prefix}}'",
+            "type-alarm": "Alarm",
+            "type-alarms": "Alarms",
+            "list-of-alarms": "{ count, select, 1 {One alarms} other {List of # alarms} }",
+            "alarm-name-starts-with": "Alarms whose names start with '{{prefix}}'",
+            "type-rulechain": "Rule chain",
+            "type-rulechains": "Rule chains",
+            "list-of-rulechains": "{ count, select, 1 {One rule chain} other {List of # rule chains} }",
+            "rulechain-name-starts-with": "Rule chains whose names start with '{{prefix}}'",
+            "type-current-customer": "Current Customer",
+            "search": "Search entities",
+            "selected-entities": "{ count, select, 1 {1 entity} other {# entities} } selected",
+            "entity-name": "Entity name",
+            "details": "Entity details",
+            "no-entities-prompt": "No entities found",
+            "no-data": "No data to display"
+        },
         "event": {
             "event-type": "이벤트 타입",
             "type-error": "에러",
             "type-lc-event": "주기적 이벤트",
             "type-stats": "통계",
+            "type-debug-rule-node": "Debug", // TODO
+            "type-debug-rule-chain": "Debug", // TODO
             "no-events-prompt": "이벤트 없음",
             "error": "에러",
             "alarm": "알람",
@@ -387,6 +692,14 @@ export default function addLocaleKorean(locales) {
             "server": "서버",
             "body": "Body",
             "method": "Method",
+            "type": "Type", // TODO
+            "entity": "Entity", // TODO
+            "message-id": "Message Id", // TODO
+            "message-type": "Message Type", // TODO
+            "data-type": "Data Type", // TODO
+            "relation-type": "Relation Type", // TODO
+            "metadata": "Metadata", // TODO
+            "data": "Data", // TODO
             "event": "이벤트",
             "status": "상태",
             "success": "성공",
@@ -394,6 +707,163 @@ export default function addLocaleKorean(locales) {
             "messages-processed": "처리된 메시지",
             "errors-occurred": "오류가 발생했습니다"
         },
+        "extension": { // TODO
+            "extensions": "Extensions",
+            "selected-extensions": "{ count, select, 1 {1 extension} other {# extensions} } selected",
+            "type": "Type",
+            "key": "Key",
+            "value": "Value",
+            "id": "Id",
+            "extension-id": "Extension id",
+            "extension-type": "Extension type",
+            "transformer-json": "JSON *",
+            "unique-id-required": "Current extension id already exists.",
+            "delete": "Delete extension",
+            "add": "Add extension",
+            "edit": "Edit extension",
+            "delete-extension-title": "Are you sure you want to delete the extension '{{extensionId}}'?",
+            "delete-extension-text": "Be careful, after the confirmation the extension and all related data will become unrecoverable.",
+            "delete-extensions-title": "Are you sure you want to delete { count, select, 1 {1 extension} other {# extensions} }?",
+            "delete-extensions-text": "Be careful, after the confirmation all selected extensions will be removed.",
+            "converters": "Converters",
+            "converter-id": "Converter id",
+            "configuration": "Configuration",
+            "converter-configurations": "Converter configurations",
+            "token": "Security token",
+            "add-converter": "Add converter",
+            "add-config": "Add converter configuration",
+            "device-name-expression": "Device name expression",
+            "device-type-expression": "Device type expression",
+            "custom": "Custom",
+            "to-double": "To Double",
+            "transformer": "Transformer",
+            "json-required": "Transformer json is required.",
+            "json-parse": "Unable to parse transformer json.",
+            "attributes": "Attributes",
+            "add-attribute": "Add attribute",
+            "add-map": "Add mapping element",
+            "timeseries": "Timeseries",
+            "add-timeseries": "Add timeseries",
+            "field-required": "Field is required",
+            "brokers": "Brokers",
+            "add-broker": "Add broker",
+            "host": "Host",
+            "port": "Port",
+            "port-range": "Port should be in a range from 1 to 65535.",
+            "ssl": "Ssl",
+            "credentials": "Credentials",
+            "username": "Username",
+            "password": "Password",
+            "retry-interval": "Retry interval in milliseconds",
+            "anonymous": "Anonymous",
+            "basic": "Basic",
+            "pem": "PEM",
+            "ca-cert": "CA certificate file *",
+            "private-key": "Private key file *",
+            "cert": "Certificate file *",
+            "no-file": "No file selected.",
+            "drop-file": "Drop a file or click to select a file to upload.",
+            "mapping": "Mapping",
+            "topic-filter": "Topic filter",
+            "converter-type": "Converter type",
+            "converter-json": "Json",
+            "json-name-expression": "Device name json expression",
+            "topic-name-expression": "Device name topic expression",
+            "json-type-expression": "Device type json expression",
+            "topic-type-expression": "Device type topic expression",
+            "attribute-key-expression": "Attribute key expression",
+            "attr-json-key-expression": "Attribute key json expression",
+            "attr-topic-key-expression": "Attribute key topic expression",
+            "request-id-expression": "Request id expression",
+            "request-id-json-expression": "Request id json expression",
+            "request-id-topic-expression": "Request id topic expression",
+            "response-topic-expression": "Response topic expression",
+            "value-expression": "Value expression",
+            "topic": "Topic",
+            "timeout": "Timeout in milliseconds",
+            "converter-json-required": "Converter json is required.",
+            "converter-json-parse": "Unable to parse converter json.",
+            "filter-expression": "Filter expression",
+            "connect-requests": "Connect requests",
+            "add-connect-request": "Add connect request",
+            "disconnect-requests": "Disconnect requests",
+            "add-disconnect-request": "Add disconnect request",
+            "attribute-requests": "Attribute requests",
+            "add-attribute-request": "Add attribute request",
+            "attribute-updates": "Attribute updates",
+            "add-attribute-update": "Add attribute update",
+            "server-side-rpc": "Server side RPC",
+            "add-server-side-rpc-request": "Add server-side RPC request",
+            "device-name-filter": "Device name filter",
+            "attribute-filter": "Attribute filter",
+            "method-filter": "Method filter",
+            "request-topic-expression": "Request topic expression",
+            "response-timeout": "Response timeout in milliseconds",
+            "topic-expression": "Topic expression",
+            "client-scope": "Client scope",
+            "add-device": "Add device",
+            "opc-server": "Servers",
+            "opc-add-server": "Add server",
+            "opc-add-server-prompt": "Please add server",
+            "opc-application-name": "Application name",
+            "opc-application-uri": "Application uri",
+            "opc-scan-period-in-seconds": "Scan period in seconds",
+            "opc-security": "Security",
+            "opc-identity": "Identity",
+            "opc-keystore": "Keystore",
+            "opc-type": "Type",
+            "opc-keystore-type": "Type",
+            "opc-keystore-location": "Location *",
+            "opc-keystore-password": "Password",
+            "opc-keystore-alias": "Alias",
+            "opc-keystore-key-password": "Key password",
+            "opc-device-node-pattern": "Device node pattern",
+            "opc-device-name-pattern": "Device name pattern",
+            "modbus-server": "Servers/slaves",
+            "modbus-add-server": "Add server/slave",
+            "modbus-add-server-prompt": "Please add server/slave",
+            "modbus-transport": "Transport",
+            "modbus-port-name": "Serial port name",
+            "modbus-encoding": "Encoding",
+            "modbus-parity": "Parity",
+            "modbus-baudrate": "Baud rate",
+            "modbus-databits": "Data bits",
+            "modbus-stopbits": "Stop bits",
+            "modbus-databits-range": "Data bits should be in a range from 7 to 8.",
+            "modbus-stopbits-range": "Stop bits should be in a range from 1 to 2.",
+            "modbus-unit-id": "Unit ID",
+            "modbus-unit-id-range": "Unit ID should be in a range from 1 to 247.",
+            "modbus-device-name": "Device name",
+            "modbus-poll-period": "Poll period (ms)",
+            "modbus-attributes-poll-period": "Attributes poll period (ms)",
+            "modbus-timeseries-poll-period": "Timeseries poll period (ms)",
+            "modbus-poll-period-range": "Poll period should be positive value.",
+            "modbus-tag": "Tag",
+            "modbus-function": "Function",
+            "modbus-register-address": "Register address",
+            "modbus-register-address-range": "Register address should be in a range from 0 to 65535.",
+            "modbus-register-bit-index": "Bit index",
+            "modbus-register-bit-index-range": "Bit index should be in a range from 0 to 15.",
+            "modbus-register-count": "Register count",
+            "modbus-register-count-range": "Register count should be a positive value.",
+            "modbus-byte-order": "Byte order",
+
+            "sync": {
+                "status": "Status",
+                "sync": "Sync",
+                "not-sync": "Not sync",
+                "last-sync-time": "Last sync time",
+                "not-available": "Not available"
+            },
+
+            "export-extensions-configuration": "Export extensions configuration",
+            "import-extensions-configuration": "Import extensions configuration",
+            "import-extensions": "Import extensions",
+            "import-extension": "Import extension",
+            "export-extension": "Export extension",
+            "file": "Extensions file",
+            "invalid-file-error": "Invalid extension file"
+        },
         "fullscreen": {
             "expand": "전체화면으로 확장",
             "exit": "전체화면 종료",
@@ -436,7 +906,24 @@ export default function addLocaleKorean(locales) {
         },
         "js-func": {
             "no-return-error": "함수는 값을 반환해야 합니다!",
-            "return-type-mismatch": "함수는 '{{type}}' 유형의 값을 반환해야 합니다!"
+            "return-type-mismatch": "함수는 '{{type}}' 유형의 값을 반환해야 합니다!",
+            "tidy": "Tidy" // TODO
+        },
+        "key-val": { // TODO
+            "key": "Key",
+            "value": "Value",
+            "remove-entry": "Remove entry",
+            "add-entry": "Add entry",
+            "no-data": "No entries"
+        },
+        "layout": { // TODO
+            "layout": "Layout",
+            "manage": "Manage layouts",
+            "settings": "Layout settings",
+            "color": "Color",
+            "main": "Main",
+            "right": "Right",
+            "select": "Select target layout"
         },
         "legend": {
             "position": "범례 위치",
@@ -467,45 +954,6 @@ export default function addLocaleKorean(locales) {
             "password-link-sent-message": "비밀번호 재설정 링크가 성공적으로 전송되었습니다!",
             "email": "이메일"
         },
-        "plugin": {
-            "plugins": "플러그인",
-            "delete": "플러그인 삭제",
-            "activate": "플러그인 활성화",
-            "suspend": "플러그인 비활성화",
-            "active": "활성화",
-            "suspended": "비활성화",
-            "name": "이름",
-            "name-required": "이름을 입력하세요.",
-            "description": "설명",
-            "add": "플러그인 추가",
-            "delete-plugin-title": "'{{pluginName}}' 플러그인을 삭제하시겠습니까?",
-            "delete-plugin-text": "플러그인과 관련된 모든 데이터를 복구할 수 없으므로 주의하십시오.",
-            "delete-plugins-title": "{ count, select, 1 {플러그인 1개} other {플러그인 #개} }를 삭제하시겠습니까?",
-            "delete-plugins-action-title": "{ count, select, 1 {플러그인 1개} other {플러그인 #개} } 삭제",
-            "delete-plugins-text": "선택된 플러그인이 삭제되고 관련된 모든 데이터가 없어지므로 주의하십시오.",
-            "add-plugin-text": "새로운 플러그인 추가",
-            "no-plugins-text": "플러그인이 없습니다.",
-            "plugin-details": "플러그인 상세정보",
-            "api-token": "API 토큰",
-            "api-token-required": "API 토큰을 입력하세요.",
-            "type": "플러그인 종류",
-            "type-required": "플러그인 종류를 선택해주세요.",
-            "configuration": "플러그인 구성",
-            "system": "시스템",
-            "select-plugin": "플러그인 선택",
-            "plugin": "플러그인",
-            "no-plugins-matching": "'{{entity}}'과 일치하는 플러그인을 찾을 수 없습니다.",
-            "plugin-required": "플러그인을 입력하세요.",
-            "plugin-require-match": "기존의 플러그인을 선택해주세요.",
-            "events": "이벤트",
-            "details": "상세",
-            "import": "플러그인 가져오기",
-            "export": "플러그인 내보내기",
-            "export-failed-error": "플러그인을 내보내기 할 수 없습니다.: {{error}}",
-            "create-new-plugin": "새로운 플러그인 생성",
-            "plugin-file": "플러그인 파일",
-            "invalid-plugin-file-error": "플러그인을 가져오기 할 수 없습니다.: 잘못된 플러그인 데이터 구조입니다."
-        },
         "position": {
             "top": "상단",
             "bottom": "하단",
@@ -517,60 +965,139 @@ export default function addLocaleKorean(locales) {
             "change-password": "비밀번호 변경",
             "current-password": "현재 비밀번호"
         },
-        "rule": {
-            "rules": "규칙",
-            "delete": "규칙 삭제",
-            "activate": "규칙 활성화",
-            "suspend": "규칙 비활성화",
-            "active": "활성화",
-            "suspended": "비활성화",
-            "name": "이름",
-            "name-required": "이름을 입력하세요.",
-            "description": "설명",
-            "add": "규칙 추가",
-            "delete-rule-title": "'{{ruleName}}' 규칙을 삭제하시겠습니까?",
-            "delete-rule-text": "규칙과 관련된 모든 데이터를 복구할 수 없으므로 주의하십시오.",
-            "delete-rules-title": "{ count, select, 1 {규칙 1개} other {규칙 #개} }를 삭제하시겠습니까?",
-            "delete-rules-action-title": "{ count, select, 1 {규칙 1개} other {규칙 #개} } 삭제",
-            "delete-rules-text": "선택된 규칙이 삭제되고 관련된 모든 데이터를 복구할 수 없으므로 주의하십시오.",
-            "add-rule-text": "규칙 추가",
-            "no-rules-text": "규칙이 없습니다.",
-            "rule-details": "규칙 상세정보",
-            "filters": "필터",
-            "filter": "필터",
-            "add-filter-prompt": "필터를 추가해 주세요.",
-            "remove-filter": "필터 삭제",
-            "add-filter": "필터 추가",
-            "filter-name": "필터 이름",
-            "filter-type": "필터 종류",
-            "edit-filter": "필터 수정",
-            "view-filter": "필터 보기",
-            "component-name": "이름",
-            "component-name-required": "이름을 입력하세요.",
-            "component-type": "종류",
-            "component-type-required": "타입을 입력하세요.",
-            "processor": "프로세서",
-            "no-processor-configured": "프로세서가 구성되지 않았습니다.",
-            "create-processor": "프로세서 생성",
-            "processor-name": "프로세서 이름",
-            "processor-type": "프로세서 종류",
-            "plugin-action": "플러그인 액션",
-            "action-name": "액션 이름",
-            "action-type": "액션 종류",
-            "create-action-prompt": "액션을 만들어 주세요",
-            "create-action": "액션 생성",
-            "details": "상세",
-            "events": "이벤트",
-            "system": "시스템",
-            "import": "규칙 가져오기",
-            "export": "규칙 내보내기",
-            "export-failed-error": "규칙을 내보내기 할 수 없습니다.: {{error}}",
-            "create-new-rule": "새로운 규칙 생성",
-            "rule-file": "규칙 파일",
-            "invalid-rule-file-error": "규칙을 가져오기 할 수 없습니다.: 잘못된 데이터 구조입니다."
+        "relation": { // TODO
+            "relations": "Relations",
+            "direction": "Direction",
+            "search-direction": {
+                "FROM": "From",
+                "TO": "To"
+            },
+            "direction-type": {
+                "FROM": "from",
+                "TO": "to"
+            },
+            "from-relations": "Outbound relations",
+            "to-relations": "Inbound relations",
+            "selected-relations": "{ count, select, 1 {1 relation} other {# relations} } selected",
+            "type": "Type",
+            "to-entity-type": "To entity type",
+            "to-entity-name": "To entity name",
+            "from-entity-type": "From entity type",
+            "from-entity-name": "From entity name",
+            "to-entity": "To entity",
+            "from-entity": "From entity",
+            "delete": "Delete relation",
+            "relation-type": "Relation type",
+            "relation-type-required": "Relation type is required.",
+            "any-relation-type": "Any type",
+            "add": "Add relation",
+            "edit": "Edit relation",
+            "delete-to-relation-title": "Are you sure you want to delete relation to the entity '{{entityName}}'?",
+            "delete-to-relation-text": "Be careful, after the confirmation the entity '{{entityName}}' will be unrelated from the current entity.",
+            "delete-to-relations-title": "Are you sure you want to delete { count, select, 1 {1 relation} other {# relations} }?",
+            "delete-to-relations-text": "Be careful, after the confirmation all selected relations will be removed and corresponding entities will be unrelated from the current entity.",
+            "delete-from-relation-title": "Are you sure you want to delete relation from the entity '{{entityName}}'?",
+            "delete-from-relation-text": "Be careful, after the confirmation current entity will be unrelated from the entity '{{entityName}}'.",
+            "delete-from-relations-title": "Are you sure you want to delete { count, select, 1 {1 relation} other {# relations} }?",
+            "delete-from-relations-text": "Be careful, after the confirmation all selected relations will be removed and current entity will be unrelated from the corresponding entities.",
+            "remove-relation-filter": "Remove relation filter",
+            "add-relation-filter": "Add relation filter",
+            "any-relation": "Any relation",
+            "relation-filters": "Relation filters",
+            "additional-info": "Additional info (JSON)",
+            "invalid-additional-info": "Unable to parse additional info json."
+        },
+        "rulechain": { // TODO
+            "rulechain": "Rule chain",
+            "rulechains": "Rule chains",
+            "root": "Root",
+            "delete": "Delete rule chain",
+            "name": "Name",
+            "name-required": "Name is required.",
+            "description": "Description",
+            "add": "Add Rule Chain",
+            "set-root": "Make rule chain root",
+            "set-root-rulechain-title": "Are you sure you want to make the rule chain '{{ruleChainName}}' root?",
+            "set-root-rulechain-text": "After the confirmation the rule chain will become root and will handle all incoming transport messages.",
+            "delete-rulechain-title": "Are you sure you want to delete the rule chain '{{ruleChainName}}'?",
+            "delete-rulechain-text": "Be careful, after the confirmation the rule chain and all related data will become unrecoverable.",
+            "delete-rulechains-title": "Are you sure you want to delete { count, select, 1 {1 rule chain} other {# rule chains} }?",
+            "delete-rulechains-action-title": "Delete { count, select, 1 {1 rule chain} other {# rule chains} }",
+            "delete-rulechains-text": "Be careful, after the confirmation all selected rule chains will be removed and all related data will become unrecoverable.",
+            "add-rulechain-text": "Add new rule chain",
+            "no-rulechains-text": "No rule chains found",
+            "rulechain-details": "Rule chain details",
+            "details": "Details",
+            "events": "Events",
+            "system": "System",
+            "import": "Import rule chain",
+            "export": "Export rule chain",
+            "export-failed-error": "Unable to export rule chain: {{error}}",
+            "create-new-rulechain": "Create new rule chain",
+            "rulechain-file": "Rule chain file",
+            "invalid-rulechain-file-error": "Unable to import rule chain: Invalid rule chain data structure.",
+            "copyId": "Copy rule chain Id",
+            "idCopiedMessage": "Rule chain Id has been copied to clipboard",
+            "select-rulechain": "Select rule chain",
+            "no-rulechains-matching": "No rule chains matching '{{entity}}' were found.",
+            "rulechain-required": "Rule chain is required",
+            "management": "Rules management",
+            "debug-mode": "Debug mode"
         },
-        "rule-plugin": {
-            "management": "규칙 및 플러그인 관리"
+        "rulenode": { // TODO
+            "details": "Details",
+            "events": "Events",
+            "search": "Search nodes",
+            "open-node-library": "Open node library",
+            "add": "Add rule node",
+            "name": "Name",
+            "name-required": "Name is required.",
+            "type": "Type",
+            "description": "Description",
+            "delete": "Delete rule node",
+            "select-all-objects": "Select all nodes and connections",
+            "deselect-all-objects": "Deselect all nodes and connections",
+            "delete-selected-objects": "Delete selected nodes and connections",
+            "delete-selected": "Delete selected",
+            "select-all": "Select all",
+            "copy-selected": "Copy selected",
+            "deselect-all": "Deselect all",
+            "rulenode-details": "Rule node details",
+            "debug-mode": "Debug mode",
+            "configuration": "Configuration",
+            "link": "Link",
+            "link-details": "Rule node link details",
+            "add-link": "Add link",
+            "link-label": "Link label",
+            "link-label-required": "Link label is required.",
+            "custom-link-label": "Custom link label",
+            "custom-link-label-required": "Custom link label is required.",
+            "type-filter": "Filter",
+            "type-filter-details": "Filter incoming messages with configured conditions",
+            "type-enrichment": "Enrichment",
+            "type-enrichment-details": "Add additional information into Message Metadata",
+            "type-transformation": "Transformation",
+            "type-transformation-details": "Change Message payload and Metadata",
+            "type-action": "Action",
+            "type-action-details": "Perform special action",
+            "type-external": "External",
+            "type-external-details": "Interacts with external system",
+            "type-rule-chain": "Rule Chain",
+            "type-rule-chain-details": "Forwards incoming messages to specified Rule Chain",
+            "type-input": "Input",
+            "type-input-details": "Logical input of Rule Chain, forwards incoming messages to next related Rule Node",
+            "directive-is-not-loaded": "Defined configuration directive '{{directiveName}}' is not available.",
+            "ui-resources-load-error": "Failed to load configuration ui resources.",
+            "invalid-target-rulechain": "Unable to resolve target rule chain!",
+            "test-script-function": "Test script function",
+            "message": "Message",
+            "message-type": "Message type",
+            "message-type-required": "Message type is required",
+            "metadata": "Metadata",
+            "metadata-required": "Metadata entries can't be empty.",
+            "output": "Output",
+            "test": "Test",
+            "help": "Help"
         },
         "tenant": {
             "tenants": "테넌트",
@@ -589,7 +1116,14 @@ export default function addLocaleKorean(locales) {
             "delete-tenants-text": "선택된 테넌트가 삭제되고 관련된 모든 정보를 복구할 수 없으므로 주의하십시오.",
             "title": "타이틀",
             "title-required": "타이틀을 입력하세요.",
-            "description": "설명"
+            "description": "설명",
+            "details": "Details", // TODO
+            "events": "Events", // TODO
+            "copyId": "Copy tenant Id", // TODO
+            "idCopiedMessage": "Tenant Id has been copied to clipboard", // TODO
+            "select-tenant": "Select tenant", // TODO
+            "no-tenants-matching": "No tenants matching '{{entity}}' were found.",
+            "tenant-required": "Tenant is required" // TODO
         },
         "timeinterval": {
             "seconds-interval": "{ seconds, select, 1 {1 second} other {# seconds} }",
@@ -642,7 +1176,18 @@ export default function addLocaleKorean(locales) {
             "last-name": "성",
             "description": "설명",
             "default-dashboard": "기본 대시보드",
-            "always-fullscreen": "항상 전체화면"
+            "always-fullscreen": "항상 전체화면",
+            "select-user": "Select user", // TODO
+            "no-users-matching": "No users matching '{{entity}}' were found.", // TODO
+            "user-required": "User is required", // TODO
+            "activation-method": "Activation method", // TODO
+            "display-activation-link": "Display activation link", // TODO
+            "send-activation-mail": "Send activation mail", // TODO
+            "activation-link": "User activation link", // TODO
+            "activation-link-text": "In order to activate user use the following <a href='{{activationLink}}' target='_blank'>activation link</a> :", // TODO
+            "copy-activation-link": "Copy activation link", // TODO
+            "activation-link-copied-message": "User activation link has been copied to clipboard", // TODO
+            "details": "Details" // TODO
         },
         "value": {
             "type": "Value type",
@@ -707,6 +1252,18 @@ export default function addLocaleKorean(locales) {
             "undo": "위젯 변경사항 취소",
             "export": "위젯 내보내기"
         },
+        "widget-action": { // TODO
+            "header-button": "Widget header button",
+            "open-dashboard-state": "Navigate to new dashboard state",
+            "update-dashboard-state": "Update current dashboard state",
+            "open-dashboard": "Navigate to other dashboard",
+            "custom": "Custom action",
+            "target-dashboard-state": "Target dashboard state",
+            "target-dashboard-state-required": "Target dashboard state is required",
+            "set-entity-from-widget": "Set entity from widget",
+            "target-dashboard": "Target dashboard",
+            "open-right-layout": "Open right dashboard layout (mobile view)"
+        },
         "widgets-bundle": {
             "current": "현재 번들",
             "widgets-bundles": "위젯 번들",
@@ -770,6 +1327,20 @@ export default function addLocaleKorean(locales) {
             "widget-type-file": "위젯 타입 파일",
             "invalid-widget-type-file-error": "위젯 타입을 가져오기 할 수 없습니다.: 잘못된 위젯 타입 데이터 구조입니다."
         },
+        "icon": { // TODO
+            "icon": "Icon",
+            "select-icon": "Select icon",
+            "material-icons": "Material icons",
+            "show-all": "Show all icons"
+        },
+        "custom": {
+            "widget-action": {
+                "action-cell-button": "Action cell button",
+                "row-click": "On row click",
+                "marker-click": "On marker click",
+                "tooltip-tag-action": "Tooltip tag action"
+            }
+        },
         "language": {
             "language": "언어",
             "en_US": "영어",
@@ -779,5 +1350,5 @@ export default function addLocaleKorean(locales) {
             "es_ES": "스페인어"
         }
     };
-    angular.extend(locales, {'ko_KR': ko_KR});
+    angular.extend(locales, { 'ko_KR': ko_KR });
 }
\ No newline at end of file
diff --git a/ui/src/app/locale/locale.constant-ru.js b/ui/src/app/locale/locale.constant-ru.js
index db66834..cff9880 100644
--- a/ui/src/app/locale/locale.constant-ru.js
+++ b/ui/src/app/locale/locale.constant-ru.js
@@ -98,6 +98,159 @@ export default function addLocaleRussian(locales) {
             "enable-tls": "Включить TLS",
             "send-test-mail": "Отправить пробное письмо"
         },
+        "alarm": { // TODO
+            "alarm": "Alarm",
+            "alarms": "Alarms",
+            "select-alarm": "Select alarm",
+            "no-alarms-matching": "No alarms matching '{{entity}}' were found.",
+            "alarm-required": "Alarm is required",
+            "alarm-status": "Alarm status",
+            "search-status": {
+                "ANY": "Any",
+                "ACTIVE": "Active",
+                "CLEARED": "Cleared",
+                "ACK": "Acknowledged",
+                "UNACK": "Unacknowledged"
+            },
+            "display-status": {
+                "ACTIVE_UNACK": "Active Unacknowledged",
+                "ACTIVE_ACK": "Active Acknowledged",
+                "CLEARED_UNACK": "Cleared Unacknowledged",
+                "CLEARED_ACK": "Cleared Acknowledged"
+            },
+            "no-alarms-prompt": "No alarms found",
+            "created-time": "Created time",
+            "type": "Type",
+            "severity": "Severity",
+            "originator": "Originator",
+            "originator-type": "Originator type",
+            "details": "Details",
+            "status": "Status",
+            "alarm-details": "Alarm details",
+            "start-time": "Start time",
+            "end-time": "End time",
+            "ack-time": "Acknowledged time",
+            "clear-time": "Cleared time",
+            "severity-critical": "Critical",
+            "severity-major": "Major",
+            "severity-minor": "Minor",
+            "severity-warning": "Warning",
+            "severity-indeterminate": "Indeterminate",
+            "acknowledge": "Acknowledge",
+            "clear": "Clear",
+            "search": "Search alarms",
+            "selected-alarms": "{ count, select, 1 {1 alarm} other {# alarms} } selected",
+            "no-data": "No data to display",
+            "polling-interval": "Alarms polling interval (sec)",
+            "polling-interval-required": "Alarms polling interval is required.",
+            "min-polling-interval-message": "At least 1 sec polling interval is allowed.",
+            "aknowledge-alarms-title": "Acknowledge { count, select, 1 {1 alarm} other {# alarms} }",
+            "aknowledge-alarms-text": "Are you sure you want to acknowledge { count, select, 1 {1 alarm} other {# alarms} }?",
+            "clear-alarms-title": "Clear { count, select, 1 {1 alarm} other {# alarms} }",
+            "clear-alarms-text": "Are you sure you want to clear { count, select, 1 {1 alarm} other {# alarms} }?"
+        },
+        "alias": { // TODO
+            "add": "Add alias",
+            "edit": "Edit alias",
+            "name": "Alias name",
+            "name-required": "Alias name is required",
+            "duplicate-alias": "Alias with same name is already exists.",
+            "filter-type-single-entity": "Single entity",
+            "filter-type-entity-list": "Entity list",
+            "filter-type-entity-name": "Entity name",
+            "filter-type-state-entity": "Entity from dashboard state",
+            "filter-type-state-entity-description": "Entity taken from dashboard state parameters",
+            "filter-type-asset-type": "Asset type",
+            "filter-type-asset-type-description": "Assets of type '{{assetType}}'",
+            "filter-type-asset-type-and-name-description": "Assets of type '{{assetType}}' and with name starting with '{{prefix}}'",
+            "filter-type-device-type": "Device type",
+            "filter-type-device-type-description": "Devices of type '{{deviceType}}'",
+            "filter-type-device-type-and-name-description": "Devices of type '{{deviceType}}' and with name starting with '{{prefix}}'",
+            "filter-type-relations-query": "Relations query",
+            "filter-type-relations-query-description": "{{entities}} that have {{relationType}} relation {{direction}} {{rootEntity}}",
+            "filter-type-asset-search-query": "Asset search query",
+            "filter-type-asset-search-query-description": "Assets with types {{assetTypes}} that have {{relationType}} relation {{direction}} {{rootEntity}}",
+            "filter-type-device-search-query": "Device search query",
+            "filter-type-device-search-query-description": "Devices with types {{deviceTypes}} that have {{relationType}} relation {{direction}} {{rootEntity}}",
+            "entity-filter": "Entity filter",
+            "resolve-multiple": "Resolve as multiple entities",
+            "filter-type": "Filter type",
+            "filter-type-required": "Filter type is required.",
+            "entity-filter-no-entity-matched": "No entities matching specified filter were found.",
+            "no-entity-filter-specified": "No entity filter specified",
+            "root-state-entity": "Use dashboard state entity as root",
+            "root-entity": "Root entity",
+            "state-entity-parameter-name": "State entity parameter name",
+            "default-state-entity": "Default state entity",
+            "default-entity-parameter-name": "By default",
+            "max-relation-level": "Max relation level",
+            "unlimited-level": "Unlimited level",
+            "state-entity": "Dashboard state entity",
+            "all-entities": "All entities",
+            "any-relation": "any"
+        },
+        "asset": { // TODO
+            "asset": "Asset",
+            "assets": "Assets",
+            "management": "Asset management",
+            "view-assets": "View Assets",
+            "add": "Add Asset",
+            "assign-to-customer": "Assign to customer",
+            "assign-asset-to-customer": "Assign Asset(s) To Customer",
+            "assign-asset-to-customer-text": "Please select the assets to assign to the customer",
+            "no-assets-text": "No assets found",
+            "assign-to-customer-text": "Please select the customer to assign the asset(s)",
+            "public": "Public",
+            "assignedToCustomer": "Assigned to customer",
+            "make-public": "Make asset public",
+            "make-private": "Make asset private",
+            "unassign-from-customer": "Unassign from customer",
+            "delete": "Delete asset",
+            "asset-public": "Asset is public",
+            "asset-type": "Asset type",
+            "asset-type-required": "Asset type is required.",
+            "select-asset-type": "Select asset type",
+            "enter-asset-type": "Enter asset type",
+            "any-asset": "Any asset",
+            "no-asset-types-matching": "No asset types matching '{{entitySubtype}}' were found.",
+            "asset-type-list-empty": "No asset types selected.",
+            "asset-types": "Asset types",
+            "name": "Name",
+            "name-required": "Name is required.",
+            "description": "Description",
+            "type": "Type",
+            "type-required": "Type is required.",
+            "details": "Details",
+            "events": "Events",
+            "add-asset-text": "Add new asset",
+            "asset-details": "Asset details",
+            "assign-assets": "Assign assets",
+            "assign-assets-text": "Assign { count, select, 1 {1 asset} other {# assets} } to customer",
+            "delete-assets": "Delete assets",
+            "unassign-assets": "Unassign assets",
+            "unassign-assets-action-title": "Unassign { count, select, 1 {1 asset} other {# assets} } from customer",
+            "assign-new-asset": "Assign new asset",
+            "delete-asset-title": "Are you sure you want to delete the asset '{{assetName}}'?",
+            "delete-asset-text": "Be careful, after the confirmation the asset and all related data will become unrecoverable.",
+            "delete-assets-title": "Are you sure you want to delete { count, select, 1 {1 asset} other {# assets} }?",
+            "delete-assets-action-title": "Delete { count, select, 1 {1 asset} other {# assets} }",
+            "delete-assets-text": "Be careful, after the confirmation all selected assets will be removed and all related data will become unrecoverable.",
+            "make-public-asset-title": "Are you sure you want to make the asset '{{assetName}}' public?",
+            "make-public-asset-text": "After the confirmation the asset and all its data will be made public and accessible by others.",
+            "make-private-asset-title": "Are you sure you want to make the asset '{{assetName}}' private?",
+            "make-private-asset-text": "After the confirmation the asset and all its data will be made private and won't be accessible by others.",
+            "unassign-asset-title": "Are you sure you want to unassign the asset '{{assetName}}'?",
+            "unassign-asset-text": "After the confirmation the asset will be unassigned and won't be accessible by the customer.",
+            "unassign-asset": "Unassign asset",
+            "unassign-assets-title": "Are you sure you want to unassign { count, select, 1 {1 asset} other {# assets} }?",
+            "unassign-assets-text": "After the confirmation all selected assets will be unassigned and won't be accessible by the customer.",
+            "copyId": "Copy asset Id",
+            "idCopiedMessage": "Asset Id has been copied to clipboard",
+            "select-asset": "Select asset",
+            "no-assets-matching": "No assets matching '{{entity}}' were found.",
+            "asset-required": "Asset is required",
+            "name-starts-with": "Asset name starts with"
+        },
         "attribute": {
             "attributes": "Атрибуты",
             "latest-telemetry": "Последняя телеметрия",
@@ -124,6 +277,38 @@ export default function addLocaleRussian(locales) {
             "selected-attributes": "{ count, plural, 1 {Выбран} other {Выбраны} } { count, plural, one {1 атрибут} few {# атрибута} other {# атрибутов} }",
             "selected-telemetry": "{ count, plural, 1 {Выбран} other {Выбраны} } { count, plural, 1 {1 параметр} few {# параметра} other {# параметров} } телеметрии"
         },
+        "audit-log": { // TODO
+            "audit": "Audit",
+            "audit-logs": "Audit Logs",
+            "timestamp": "Timestamp",
+            "entity-type": "Entity Type",
+            "entity-name": "Entity Name",
+            "user": "User",
+            "type": "Type",
+            "status": "Status",
+            "details": "Details",
+            "type-added": "Added",
+            "type-deleted": "Deleted",
+            "type-updated": "Updated",
+            "type-attributes-updated": "Attributes updated",
+            "type-attributes-deleted": "Attributes deleted",
+            "type-rpc-call": "RPC call",
+            "type-credentials-updated": "Credentials updated",
+            "type-assigned-to-customer": "Assigned to Customer",
+            "type-unassigned-from-customer": "Unassigned from Customer",
+            "type-activated": "Activated",
+            "type-suspended": "Suspended",
+            "type-credentials-read": "Credentials read",
+            "type-attributes-read": "Attributes read",
+            "status-success": "Success",
+            "status-failure": "Failure",
+            "audit-log-details": "Audit log details",
+            "no-audit-logs-prompt": "No logs found",
+            "action-data": "Action data",
+            "failure-details": "Failure details",
+            "search": "Search audit logs",
+            "clear-search": "Clear search"
+        },
         "confirm-on-exit": {
             "message": "У вас есть несохраненные изменения. Вы точно хотите покинуть эту страницу?",
             "html-message": "У вас есть несохраненные изменения.<br/>Вы точно хотите покинуть эту страницу?",
@@ -148,6 +333,11 @@ export default function addLocaleRussian(locales) {
             "enter-password": "Введите пароль",
             "enter-search": "Введите условие поиска"
         },
+        "content-type": { // TODO
+            "json": "Json",
+            "text": "Text",
+            "binary": "Binary (Base64)"
+        },
         "customer": {
             "customers": "Клиенты",
             "management": "Управление клиентами",
@@ -172,11 +362,22 @@ export default function addLocaleRussian(locales) {
             "delete-customers-action-title": "Удалить { count, plural, one {1 клиента} other {# клиентов} } }",
             "delete-customers-text": "Внимание, после подтверждения клиенты и вся связанная с ними информация будут безвозвратно утеряны.",
             "manage-users": "Управление пользователями",
+            "manage-assets": "Manage assets", // TODO
             "manage-devices": "Управление устройствами",
             "manage-dashboards": "Управление дашбордами",
             "title": "Имя",
             "title-required": "Название обязательно.",
-            "description": "Описание"
+            "description": "Описание",
+            "details": "Details", // TODO
+            "events": "Events", // TODO
+            "copyId": "Copy customer Id", // TODO
+            "idCopiedMessage": "Customer Id has been copied to clipboard", // TODO
+            "select-customer": "Select customer", // TODO
+            "no-customers-matching": "No customers matching '{{entity}}' were found.", // TODO
+            "customer-required": "Customer is required", // TODO
+            "select-default-customer": "Select default customer", // TODO
+            "default-customer": "Default customer", // TODO
+            "default-customer-required": "Default customer is required in order to debug dashboard on Tenant level" // TODO
         },
         "datetime": {
             "date-from": "Дата с",
@@ -282,19 +483,41 @@ export default function addLocaleRussian(locales) {
             "configuration-error": "Ошибка конфигурирования",
             "alias-resolution-error-title": "Ошибка конфигурирования псевдонимов дашборда",
             "invalid-aliases-config": "Не удалось найти устройства, соответствующие фильтру псевдонимов.<br/>" +
-                                      "Пожалуйста, свяжитесь с администратором для устранения этой проблемы.",
+                "Пожалуйста, свяжитесь с администратором для устранения этой проблемы.",
             "select-devices": "Выберите устройства",
             "assignedToCustomer": "Прикреплен к клиенту",
             "public": "Общедоступный",
             "public-link": "Общедоступная ссылка",
             "copy-public-link": "Скопировать общедоступную ссылку",
-            "public-link-copied-message": "Общедоступная ссылка на дашборд скопирована в буфер обмена"
+            "public-link-copied-message": "Общедоступная ссылка на дашборд скопирована в буфер обмена",
+            "manage-states": "Manage dashboard states", // TODO
+            "states": "Dashboard states", // TODO
+            "search-states": "Search dashboard states", // TODO
+            "selected-states": "{ count, select, 1 {1 dashboard state} other {# dashboard states} } selected", // TODO
+            "edit-state": "Edit dashboard state", // TODO
+            "delete-state": "Delete dashboard state", // TODO
+            "add-state": "Add dashboard state", // TODO
+            "state": "Dashboard state", // TODO
+            "state-name": "Name", // TODO
+            "state-name-required": "Dashboard state name is required.", // TODO
+            "state-id": "State Id", // TODO
+            "state-id-required": "Dashboard state id is required.", // TODO
+            "state-id-exists": "Dashboard state with the same id is already exists.", // TODO
+            "is-root-state": "Root state", // TODO
+            "delete-state-title": "Delete dashboard state", // TODO
+            "delete-state-text": "Are you sure you want delete dashboard state with name '{{stateName}}'?", // TODO
+            "show-details": "Show details", // TODO
+            "hide-details": "Hide details", // TODO
+            "select-state": "Select target state", // TODO
+            "state-controller": "State controller" // TODO
         },
         "datakey": {
             "settings": "Настройки",
             "advanced": "Дополнительно",
             "label": "Метка",
             "color": "Цвет",
+            "units": "Special symbol to show next to value", // TODO
+            "decimals": "Number of digits after floating point", // TODO
             "data-generation-func": "Функция генерации данных",
             "use-data-post-processing-func": "Использовать функцию пост-обработки данных",
             "configuration": "Конфигурация ключа данных",
@@ -302,8 +525,11 @@ export default function addLocaleRussian(locales) {
             "attributes": "Атрибуты",
             "timeseries-required": "Выборка по времени обязательна.",
             "timeseries-or-attributes-required": "Выборка по времени/атрибуты обязательны.",
+            "maximum-timeseries-or-attributes": "Maximum { count, select, 1 {1 timeseries/attribute is allowed.} other {# timeseries/attributes are allowed} }", // TODO
+            "alarm-fields-required": "Alarm fields are required.", // TODO
             "function-types": "Тип функции",
-            "function-types-required": "Тип функции обязателен."
+            "function-types-required": "Тип функции обязателен.",
+            "maximum-function-types": "Maximum { count, select, 1 {1 function type is allowed.} other {# function types are allowed} }" // TODO
         },
         "datasource": {
             "type": "Тип источника данных",
@@ -408,11 +634,101 @@ export default function addLocaleRussian(locales) {
             "unhandled-error-code": "Код необработанной ошибки: {{errorCode}}",
             "unknown-error": "Неизвестная ошибка"
         },
+        "entity": { // TODO
+            "entity": "Entity",
+            "entities": "Entities",
+            "aliases": "Entity aliases",
+            "entity-alias": "Entity alias",
+            "unable-delete-entity-alias-title": "Unable to delete entity alias",
+            "unable-delete-entity-alias-text": "Entity alias '{{entityAlias}}' can't be deleted as it used by the following widget(s):<br/>{{widgetsList}}",
+            "duplicate-alias-error": "Duplicate alias found '{{alias}}'.<br>Entity aliases must be unique whithin the dashboard.",
+            "missing-entity-filter-error": "Filter is missing for alias '{{alias}}'.",
+            "configure-alias": "Configure '{{alias}}' alias",
+            "alias": "Alias",
+            "alias-required": "Entity alias is required.",
+            "remove-alias": "Remove entity alias",
+            "add-alias": "Add entity alias",
+            "entity-list": "Entity list",
+            "entity-type": "Entity type",
+            "entity-types": "Entity types",
+            "entity-type-list": "Entity type list",
+            "any-entity": "Any entity",
+            "enter-entity-type": "Enter entity type",
+            "no-entities-matching": "No entities matching '{{entity}}' were found.",
+            "no-entity-types-matching": "No entity types matching '{{entityType}}' were found.",
+            "name-starts-with": "Name starts with",
+            "use-entity-name-filter": "Use filter",
+            "entity-list-empty": "No entities selected.",
+            "entity-type-list-empty": "No entity types selected.",
+            "entity-name-filter-required": "Entity name filter is required.",
+            "entity-name-filter-no-entity-matched": "No entities starting with '{{entity}}' were found.",
+            "all-subtypes": "All",
+            "select-entities": "Select entities",
+            "no-aliases-found": "No aliases found.",
+            "no-alias-matching": "'{{alias}}' not found.",
+            "create-new-alias": "Create a new one!",
+            "key": "Key",
+            "key-name": "Key name",
+            "no-keys-found": "No keys found.",
+            "no-key-matching": "'{{key}}' not found.",
+            "create-new-key": "Create a new one!",
+            "type": "Type",
+            "type-required": "Entity type is required.",
+            "type-device": "Device",
+            "type-devices": "Devices",
+            "list-of-devices": "{ count, select, 1 {One device} other {List of # devices} }",
+            "device-name-starts-with": "Devices whose names start with '{{prefix}}'",
+            "type-asset": "Asset",
+            "type-assets": "Assets",
+            "list-of-assets": "{ count, select, 1 {One asset} other {List of # assets} }",
+            "asset-name-starts-with": "Assets whose names start with '{{prefix}}'",
+            "type-rule": "Rule",
+            "type-rules": "Rules",
+            "list-of-rules": "{ count, select, 1 {One rule} other {List of # rules} }",
+            "rule-name-starts-with": "Rules whose names start with '{{prefix}}'",
+            "type-plugin": "Plugin",
+            "type-plugins": "Plugins",
+            "list-of-plugins": "{ count, select, 1 {One plugin} other {List of # plugins} }",
+            "plugin-name-starts-with": "Plugins whose names start with '{{prefix}}'",
+            "type-tenant": "Tenant",
+            "type-tenants": "Tenants",
+            "list-of-tenants": "{ count, select, 1 {One tenant} other {List of # tenants} }",
+            "tenant-name-starts-with": "Tenants whose names start with '{{prefix}}'",
+            "type-customer": "Customer",
+            "type-customers": "Customers",
+            "list-of-customers": "{ count, select, 1 {One customer} other {List of # customers} }",
+            "customer-name-starts-with": "Customers whose names start with '{{prefix}}'",
+            "type-user": "User",
+            "type-users": "Users",
+            "list-of-users": "{ count, select, 1 {One user} other {List of # users} }",
+            "user-name-starts-with": "Users whose names start with '{{prefix}}'",
+            "type-dashboard": "Dashboard",
+            "type-dashboards": "Dashboards",
+            "list-of-dashboards": "{ count, select, 1 {One dashboard} other {List of # dashboards} }",
+            "dashboard-name-starts-with": "Dashboards whose names start with '{{prefix}}'",
+            "type-alarm": "Alarm",
+            "type-alarms": "Alarms",
+            "list-of-alarms": "{ count, select, 1 {One alarms} other {List of # alarms} }",
+            "alarm-name-starts-with": "Alarms whose names start with '{{prefix}}'",
+            "type-rulechain": "Rule chain",
+            "type-rulechains": "Rule chains",
+            "list-of-rulechains": "{ count, select, 1 {One rule chain} other {List of # rule chains} }",
+            "rulechain-name-starts-with": "Rule chains whose names start with '{{prefix}}'",
+            "type-current-customer": "Current Customer",
+            "search": "Search entities",
+            "selected-entities": "{ count, select, 1 {1 entity} other {# entities} } selected",
+            "entity-name": "Entity name",
+            "details": "Entity details",
+            "no-entities-prompt": "No entities found",
+            "no-data": "No data to display"
+        },
         "event": {
             "event-type": "Тип события",
             "type-error": "Ошибка",
             "type-lc-event": "Событие жизненного цикла",
             "type-stats": "Статистика",
+            "type-debug-rule-node": "Debug", // TODO
+            "type-debug-rule-chain": "Debug", // TODO
             "no-events-prompt": "События не найдены",
             "error": "Ошибка",
             "alarm": "Аварийное оповещение",
@@ -420,6 +736,14 @@ export default function addLocaleRussian(locales) {
             "server": "Сервер",
             "body": "Тело",
             "method": "Метод",
+            "type": "Type", // TODO
+            "entity": "Entity", // TODO
+            "message-id": "Message Id", // TODO
+            "message-type": "Message Type", // TODO
+            "data-type": "Data Type", // TODO
+            "relation-type": "Relation Type", // TODO
+            "metadata": "Metadata", // TODO
+            "data": "Data", // TODO
             "event": "Событие",
             "status": "Статус",
             "success": "Успех",
@@ -427,6 +751,163 @@ export default function addLocaleRussian(locales) {
             "messages-processed": "Сообщения обработаны",
             "errors-occurred": "Возникли ошибки"
         },
+        "extension": { // TODO
+            "extensions": "Extensions",
+            "selected-extensions": "{ count, select, 1 {1 extension} other {# extensions} } selected",
+            "type": "Type",
+            "key": "Key",
+            "value": "Value",
+            "id": "Id",
+            "extension-id": "Extension id",
+            "extension-type": "Extension type",
+            "transformer-json": "JSON *",
+            "unique-id-required": "Current extension id already exists.",
+            "delete": "Delete extension",
+            "add": "Add extension",
+            "edit": "Edit extension",
+            "delete-extension-title": "Are you sure you want to delete the extension '{{extensionId}}'?",
+            "delete-extension-text": "Be careful, after the confirmation the extension and all related data will become unrecoverable.",
+            "delete-extensions-title": "Are you sure you want to delete { count, select, 1 {1 extension} other {# extensions} }?",
+            "delete-extensions-text": "Be careful, after the confirmation all selected extensions will be removed.",
+            "converters": "Converters",
+            "converter-id": "Converter id",
+            "configuration": "Configuration",
+            "converter-configurations": "Converter configurations",
+            "token": "Security token",
+            "add-converter": "Add converter",
+            "add-config": "Add converter configuration",
+            "device-name-expression": "Device name expression",
+            "device-type-expression": "Device type expression",
+            "custom": "Custom",
+            "to-double": "To Double",
+            "transformer": "Transformer",
+            "json-required": "Transformer json is required.",
+            "json-parse": "Unable to parse transformer json.",
+            "attributes": "Attributes",
+            "add-attribute": "Add attribute",
+            "add-map": "Add mapping element",
+            "timeseries": "Timeseries",
+            "add-timeseries": "Add timeseries",
+            "field-required": "Field is required",
+            "brokers": "Brokers",
+            "add-broker": "Add broker",
+            "host": "Host",
+            "port": "Port",
+            "port-range": "Port should be in a range from 1 to 65535.",
+            "ssl": "Ssl",
+            "credentials": "Credentials",
+            "username": "Username",
+            "password": "Password",
+            "retry-interval": "Retry interval in milliseconds",
+            "anonymous": "Anonymous",
+            "basic": "Basic",
+            "pem": "PEM",
+            "ca-cert": "CA certificate file *",
+            "private-key": "Private key file *",
+            "cert": "Certificate file *",
+            "no-file": "No file selected.",
+            "drop-file": "Drop a file or click to select a file to upload.",
+            "mapping": "Mapping",
+            "topic-filter": "Topic filter",
+            "converter-type": "Converter type",
+            "converter-json": "Json",
+            "json-name-expression": "Device name json expression",
+            "topic-name-expression": "Device name topic expression",
+            "json-type-expression": "Device type json expression",
+            "topic-type-expression": "Device type topic expression",
+            "attribute-key-expression": "Attribute key expression",
+            "attr-json-key-expression": "Attribute key json expression",
+            "attr-topic-key-expression": "Attribute key topic expression",
+            "request-id-expression": "Request id expression",
+            "request-id-json-expression": "Request id json expression",
+            "request-id-topic-expression": "Request id topic expression",
+            "response-topic-expression": "Response topic expression",
+            "value-expression": "Value expression",
+            "topic": "Topic",
+            "timeout": "Timeout in milliseconds",
+            "converter-json-required": "Converter json is required.",
+            "converter-json-parse": "Unable to parse converter json.",
+            "filter-expression": "Filter expression",
+            "connect-requests": "Connect requests",
+            "add-connect-request": "Add connect request",
+            "disconnect-requests": "Disconnect requests",
+            "add-disconnect-request": "Add disconnect request",
+            "attribute-requests": "Attribute requests",
+            "add-attribute-request": "Add attribute request",
+            "attribute-updates": "Attribute updates",
+            "add-attribute-update": "Add attribute update",
+            "server-side-rpc": "Server side RPC",
+            "add-server-side-rpc-request": "Add server-side RPC request",
+            "device-name-filter": "Device name filter",
+            "attribute-filter": "Attribute filter",
+            "method-filter": "Method filter",
+            "request-topic-expression": "Request topic expression",
+            "response-timeout": "Response timeout in milliseconds",
+            "topic-expression": "Topic expression",
+            "client-scope": "Client scope",
+            "add-device": "Add device",
+            "opc-server": "Servers",
+            "opc-add-server": "Add server",
+            "opc-add-server-prompt": "Please add server",
+            "opc-application-name": "Application name",
+            "opc-application-uri": "Application uri",
+            "opc-scan-period-in-seconds": "Scan period in seconds",
+            "opc-security": "Security",
+            "opc-identity": "Identity",
+            "opc-keystore": "Keystore",
+            "opc-type": "Type",
+            "opc-keystore-type": "Type",
+            "opc-keystore-location": "Location *",
+            "opc-keystore-password": "Password",
+            "opc-keystore-alias": "Alias",
+            "opc-keystore-key-password": "Key password",
+            "opc-device-node-pattern": "Device node pattern",
+            "opc-device-name-pattern": "Device name pattern",
+            "modbus-server": "Servers/slaves",
+            "modbus-add-server": "Add server/slave",
+            "modbus-add-server-prompt": "Please add server/slave",
+            "modbus-transport": "Transport",
+            "modbus-port-name": "Serial port name",
+            "modbus-encoding": "Encoding",
+            "modbus-parity": "Parity",
+            "modbus-baudrate": "Baud rate",
+            "modbus-databits": "Data bits",
+            "modbus-stopbits": "Stop bits",
+            "modbus-databits-range": "Data bits should be in a range from 7 to 8.",
+            "modbus-stopbits-range": "Stop bits should be in a range from 1 to 2.",
+            "modbus-unit-id": "Unit ID",
+            "modbus-unit-id-range": "Unit ID should be in a range from 1 to 247.",
+            "modbus-device-name": "Device name",
+            "modbus-poll-period": "Poll period (ms)",
+            "modbus-attributes-poll-period": "Attributes poll period (ms)",
+            "modbus-timeseries-poll-period": "Timeseries poll period (ms)",
+            "modbus-poll-period-range": "Poll period should be positive value.",
+            "modbus-tag": "Tag",
+            "modbus-function": "Function",
+            "modbus-register-address": "Register address",
+            "modbus-register-address-range": "Register address should be in a range from 0 to 65535.",
+            "modbus-register-bit-index": "Bit index",
+            "modbus-register-bit-index-range": "Bit index should be in a range from 0 to 15.",
+            "modbus-register-count": "Register count",
+            "modbus-register-count-range": "Register count should be a positive value.",
+            "modbus-byte-order": "Byte order",
+
+            "sync": {
+                "status": "Status",
+                "sync": "Sync",
+                "not-sync": "Not sync",
+                "last-sync-time": "Last sync time",
+                "not-available": "Not available"
+            },
+
+            "export-extensions-configuration": "Export extensions configuration",
+            "import-extensions-configuration": "Import extensions configuration",
+            "import-extensions": "Import extensions",
+            "import-extension": "Import extension",
+            "export-extension": "Export extension",
+            "file": "Extensions file",
+            "invalid-file-error": "Invalid extension file"
+        },
         "fullscreen": {
             "expand": "Во весь экран",
             "exit": "Выйти из полноэкранного режима",
@@ -469,7 +950,24 @@ export default function addLocaleRussian(locales) {
         },
         "js-func": {
             "no-return-error": "Функция должна возвращать значение!",
-            "return-type-mismatch": "Функция должна возвращать значение типа '{{type}}'!"
+            "return-type-mismatch": "Функция должна возвращать значение типа '{{type}}'!",
+            "tidy": "Tidy" // TODO
+        },
+        "key-val": { // TODO
+            "key": "Key",
+            "value": "Value",
+            "remove-entry": "Remove entry",
+            "add-entry": "Add entry",
+            "no-data": "No entries"
+        },
+        "layout": { // TODO
+            "layout": "Layout",
+            "manage": "Manage layouts",
+            "settings": "Layout settings",
+            "color": "Color",
+            "main": "Main",
+            "right": "Right",
+            "select": "Select target layout"
         },
         "legend": {
             "position": "Расположение легенды",
@@ -500,45 +998,6 @@ export default function addLocaleRussian(locales) {
             "password-link-sent-message": "Ссылка для сброса пароля была успешно отправлена!",
             "email": "Эл. адрес"
         },
-        "plugin": {
-            "plugins": "Плагины",
-            "delete": "Удалить плагин",
-            "activate": "Активировать плагин",
-            "suspend": "Приостановить плагин",
-            "active": "Активный",
-            "suspended": "Приостановлен",
-            "name": "Название",
-            "name-required": "Название обязательно.",
-            "description": "Описание",
-            "add": "Добавить плагин",
-            "delete-plugin-title": "Вы точно хотите удалить плагин '{{pluginName}}'?",
-            "delete-plugin-text": "Внимание, после подтверждения плагин и все связанные с ним данные будут безвозвратно утеряны.",
-            "delete-plugins-title": "Вы точно хотите удалить { count, plural, one {1 плагин} few {# плагина} other {# плагинов} }?",
-            "delete-plugins-action-title": "Удалить { count, plural, one {1 плагин} few {# плагина} other {# плагинов} } }",
-            "delete-plugins-text": "Внимание, после подтверждения выбранные плагины и все связанные с ними данные будут безвозвратно утеряны.",
-            "add-plugin-text": "Добавить новый плагин",
-            "no-plugins-text": "Плагины не найдены",
-            "plugin-details": "Подробности о плагине",
-            "api-token": "API токен",
-            "api-token-required": "API токен обязателен.",
-            "type": "Тип плагина",
-            "type-required": "Тип плагина обязателен.",
-            "configuration": "Настройки плагина",
-            "system": "Системный",
-            "select-plugin": "Выберите плагин",
-            "plugin": "Плагин",
-            "no-plugins-matching": "Плагин '{{entity}}' не найден.",
-            "plugin-required": "Плагин обязателен.",
-            "plugin-require-match": "Пожалуйста, выберите существующий плагин.",
-            "events": "События",
-            "details": "Подробности",
-            "import": "Импортировать плагин",
-            "export": "Экспортировать плагин",
-            "export-failed-error": "Не удалось экспортировать плагин: {{error}}",
-            "create-new-plugin": "Создать новый плагин",
-            "plugin-file": "Файл плагина",
-            "invalid-plugin-file-error": "Не удалось импортировать плагин: неизвестная схема данных плагина."
-        },
         "position": {
             "top": "Верх",
             "bottom": "Низ",
@@ -550,60 +1009,139 @@ export default function addLocaleRussian(locales) {
             "change-password": "Изменить пароль",
             "current-password": "Текущий пароль"
         },
-        "rule": {
-            "rules": "Правила",
-            "delete": "Удалить правило",
-            "activate": "Активировать правило",
-            "suspend": "Приостановить правило",
-            "active": "Активное",
-            "suspended": "Приостановлены",
-            "name": "Название",
-            "name-required": "Название обязательно.",
-            "description": "Описание",
-            "add": "Добавить правило",
-            "delete-rule-title": "Вы точно хотите удалить правило '{{ruleName}}'?",
-            "delete-rule-text": "Внимание, после подтверждения правило и все связанные с ним данные будут безвозвратно утеряны.",
-            "delete-rules-title": "Вы точно хотите удалить { count, plural, one {1 правило} few {# правила} other {# правил} }?",
-            "delete-rules-action-title": "Удалить { count, plural, one {1 правило} few {# правила} other {# правил} }",
-            "delete-rules-text": "Внимание, после подтверждения выбранные правила и все связанные с ними данные будут безвозвратно утеряны.",
-            "add-rule-text": "Добавить новое правило",
-            "no-rules-text": "Правила не найдены",
-            "rule-details": "Подробности о правиле",
-            "filters": "Фильтры",
-            "filter": "Фильтр",
-            "add-filter-prompt": "Пожалуйста, добавьте фильтр",
-            "remove-filter": "Удалить фильтр",
-            "add-filter": "Добавить фильтр",
-            "filter-name": "Название фильтра",
-            "filter-type": "Тип фильтра",
-            "edit-filter": "Редактировать фильтр",
-            "view-filter": "Просмотреть фильтр",
-            "component-name": "Название",
-            "component-name-required": "Название обязательно.",
-            "component-type": "Тип",
-            "component-type-required": "Тип обязателен.",
-            "processor": "Обработчик",
-            "no-processor-configured": "Обработчики не сконфигурированы",
-            "create-processor": "Создать обработчик",
-            "processor-name": "Название обработчика",
-            "processor-type": "Тип обработчика",
-            "plugin-action": "Действие плагина",
-            "action-name": "Название действия",
-            "action-type": "Тип действия",
-            "create-action-prompt": "Пожалуйста, создайте действие",
-            "create-action": "Создать действие",
-            "details": "Подробности",
-            "events": "События",
-            "system": "Системное",
-            "import": "Импортировать правило",
-            "export": "Экспортировать правило",
-            "export-failed-error": "Не удалось экспортировать правило: {{error}}",
-            "create-new-rule": "Создать новое правило",
-            "rule-file": "Файл правила",
-            "invalid-rule-file-error": "Не удалось импортировать правило: неизвестная схема данных правила."
+        "relation": { // TODO
+            "relations": "Relations",
+            "direction": "Direction",
+            "search-direction": {
+                "FROM": "From",
+                "TO": "To"
+            },
+            "direction-type": {
+                "FROM": "from",
+                "TO": "to"
+            },
+            "from-relations": "Outbound relations",
+            "to-relations": "Inbound relations",
+            "selected-relations": "{ count, select, 1 {1 relation} other {# relations} } selected",
+            "type": "Type",
+            "to-entity-type": "To entity type",
+            "to-entity-name": "To entity name",
+            "from-entity-type": "From entity type",
+            "from-entity-name": "From entity name",
+            "to-entity": "To entity",
+            "from-entity": "From entity",
+            "delete": "Delete relation",
+            "relation-type": "Relation type",
+            "relation-type-required": "Relation type is required.",
+            "any-relation-type": "Any type",
+            "add": "Add relation",
+            "edit": "Edit relation",
+            "delete-to-relation-title": "Are you sure you want to delete relation to the entity '{{entityName}}'?",
+            "delete-to-relation-text": "Be careful, after the confirmation the entity '{{entityName}}' will be unrelated from the current entity.",
+            "delete-to-relations-title": "Are you sure you want to delete { count, select, 1 {1 relation} other {# relations} }?",
+            "delete-to-relations-text": "Be careful, after the confirmation all selected relations will be removed and corresponding entities will be unrelated from the current entity.",
+            "delete-from-relation-title": "Are you sure you want to delete relation from the entity '{{entityName}}'?",
+            "delete-from-relation-text": "Be careful, after the confirmation current entity will be unrelated from the entity '{{entityName}}'.",
+            "delete-from-relations-title": "Are you sure you want to delete { count, select, 1 {1 relation} other {# relations} }?",
+            "delete-from-relations-text": "Be careful, after the confirmation all selected relations will be removed and current entity will be unrelated from the corresponding entities.",
+            "remove-relation-filter": "Remove relation filter",
+            "add-relation-filter": "Add relation filter",
+            "any-relation": "Any relation",
+            "relation-filters": "Relation filters",
+            "additional-info": "Additional info (JSON)",
+            "invalid-additional-info": "Unable to parse additional info json."
+        },
+        "rulechain": { // TODO
+            "rulechain": "Rule chain",
+            "rulechains": "Rule chains",
+            "root": "Root",
+            "delete": "Delete rule chain",
+            "name": "Name",
+            "name-required": "Name is required.",
+            "description": "Description",
+            "add": "Add Rule Chain",
+            "set-root": "Make rule chain root",
+            "set-root-rulechain-title": "Are you sure you want to make the rule chain '{{ruleChainName}}' root?",
+            "set-root-rulechain-text": "After the confirmation the rule chain will become root and will handle all incoming transport messages.",
+            "delete-rulechain-title": "Are you sure you want to delete the rule chain '{{ruleChainName}}'?",
+            "delete-rulechain-text": "Be careful, after the confirmation the rule chain and all related data will become unrecoverable.",
+            "delete-rulechains-title": "Are you sure you want to delete { count, select, 1 {1 rule chain} other {# rule chains} }?",
+            "delete-rulechains-action-title": "Delete { count, select, 1 {1 rule chain} other {# rule chains} }",
+            "delete-rulechains-text": "Be careful, after the confirmation all selected rule chains will be removed and all related data will become unrecoverable.",
+            "add-rulechain-text": "Add new rule chain",
+            "no-rulechains-text": "No rule chains found",
+            "rulechain-details": "Rule chain details",
+            "details": "Details",
+            "events": "Events",
+            "system": "System",
+            "import": "Import rule chain",
+            "export": "Export rule chain",
+            "export-failed-error": "Unable to export rule chain: {{error}}",
+            "create-new-rulechain": "Create new rule chain",
+            "rulechain-file": "Rule chain file",
+            "invalid-rulechain-file-error": "Unable to import rule chain: Invalid rule chain data structure.",
+            "copyId": "Copy rule chain Id",
+            "idCopiedMessage": "Rule chain Id has been copied to clipboard",
+            "select-rulechain": "Select rule chain",
+            "no-rulechains-matching": "No rule chains matching '{{entity}}' were found.",
+            "rulechain-required": "Rule chain is required",
+            "management": "Rules management",
+            "debug-mode": "Debug mode"
         },
-        "rule-plugin": {
-            "management": "Управление плагинами и правилами"
+        "rulenode": { // TODO
+            "details": "Details",
+            "events": "Events",
+            "search": "Search nodes",
+            "open-node-library": "Open node library",
+            "add": "Add rule node",
+            "name": "Name",
+            "name-required": "Name is required.",
+            "type": "Type",
+            "description": "Description",
+            "delete": "Delete rule node",
+            "select-all-objects": "Select all nodes and connections",
+            "deselect-all-objects": "Deselect all nodes and connections",
+            "delete-selected-objects": "Delete selected nodes and connections",
+            "delete-selected": "Delete selected",
+            "select-all": "Select all",
+            "copy-selected": "Copy selected",
+            "deselect-all": "Deselect all",
+            "rulenode-details": "Rule node details",
+            "debug-mode": "Debug mode",
+            "configuration": "Configuration",
+            "link": "Link",
+            "link-details": "Rule node link details",
+            "add-link": "Add link",
+            "link-label": "Link label",
+            "link-label-required": "Link label is required.",
+            "custom-link-label": "Custom link label",
+            "custom-link-label-required": "Custom link label is required.",
+            "type-filter": "Filter",
+            "type-filter-details": "Filter incoming messages with configured conditions",
+            "type-enrichment": "Enrichment",
+            "type-enrichment-details": "Add additional information into Message Metadata",
+            "type-transformation": "Transformation",
+            "type-transformation-details": "Change Message payload and Metadata",
+            "type-action": "Action",
+            "type-action-details": "Perform special action",
+            "type-external": "External",
+            "type-external-details": "Interacts with external system",
+            "type-rule-chain": "Rule Chain",
+            "type-rule-chain-details": "Forwards incoming messages to specified Rule Chain",
+            "type-input": "Input",
+            "type-input-details": "Logical input of Rule Chain, forwards incoming messages to next related Rule Node",
+            "directive-is-not-loaded": "Defined configuration directive '{{directiveName}}' is not available.",
+            "ui-resources-load-error": "Failed to load configuration ui resources.",
+            "invalid-target-rulechain": "Unable to resolve target rule chain!",
+            "test-script-function": "Test script function",
+            "message": "Message",
+            "message-type": "Message type",
+            "message-type-required": "Message type is required",
+            "metadata": "Metadata",
+            "metadata-required": "Metadata entries can't be empty.",
+            "output": "Output",
+            "test": "Test",
+            "help": "Help"
         },
         "tenant": {
             "tenants": "Владельцы",
@@ -740,6 +1278,18 @@ export default function addLocaleRussian(locales) {
             "undo": "Откатить изменения в виджете",
             "export": "Экспортировать виджет"
         },
+        "widget-action": { // TODO
+            "header-button": "Widget header button",
+            "open-dashboard-state": "Navigate to new dashboard state",
+            "update-dashboard-state": "Update current dashboard state",
+            "open-dashboard": "Navigate to other dashboard",
+            "custom": "Custom action",
+            "target-dashboard-state": "Target dashboard state",
+            "target-dashboard-state-required": "Target dashboard state is required",
+            "set-entity-from-widget": "Set entity from widget",
+            "target-dashboard": "Target dashboard",
+            "open-right-layout": "Open right dashboard layout (mobile view)"
+        },
         "widgets-bundle": {
             "current": "Текущий набор",
             "widgets-bundles": "Наборы виджетов",
@@ -803,6 +1353,20 @@ export default function addLocaleRussian(locales) {
             "widget-type-file": "Файл типа виджета",
             "invalid-widget-type-file-error": "Не удалось импортировать виджет: неизвестная схема данных типа виджета."
         },
+        "icon": { // TODO
+            "icon": "Icon",
+            "select-icon": "Select icon",
+            "material-icons": "Material icons",
+            "show-all": "Show all icons"
+        },
+        "custom": { // TODO
+            "widget-action": {
+                "action-cell-button": "Action cell button",
+                "row-click": "On row click",
+                "marker-click": "On marker click",
+                "tooltip-tag-action": "Tooltip tag action"
+            }
+        },
         "language": {
             "language": "Язык",
             "en_US": "Английский",
@@ -813,5 +1377,5 @@ export default function addLocaleRussian(locales) {
 
         }
     };
-    angular.extend(locales, {'ru_RU': ru_RU});
+    angular.extend(locales, { 'ru_RU': ru_RU });
 }
\ No newline at end of file
diff --git a/ui/src/app/locale/locale.constant-zh.js b/ui/src/app/locale/locale.constant-zh.js
index c25291a..9a08ca1 100644
--- a/ui/src/app/locale/locale.constant-zh.js
+++ b/ui/src/app/locale/locale.constant-zh.js
@@ -336,6 +336,11 @@ export default function addLocaleChinese(locales) {
             "enter-password": "输入密码",
             "enter-search": "输入检索条件"
         },
+        "content-type": { // TODO
+            "json": "Json",
+            "text": "Text",
+            "binary": "Binary (Base64)"
+        },
         "customer": {
             "customer": "客户",
             "customers": "客户",
@@ -754,6 +759,163 @@ export default function addLocaleChinese(locales) {
             "messages-processed": "消息处理",
             "errors-occurred": "错误发生"
         },
+        "extension": { // TODO
+            "extensions": "Extensions",
+            "selected-extensions": "{ count, select, 1 {1 extension} other {# extensions} } selected",
+            "type": "Type",
+            "key": "Key",
+            "value": "Value",
+            "id": "Id",
+            "extension-id": "Extension id",
+            "extension-type": "Extension type",
+            "transformer-json": "JSON *",
+            "unique-id-required": "Current extension id already exists.",
+            "delete": "Delete extension",
+            "add": "Add extension",
+            "edit": "Edit extension",
+            "delete-extension-title": "Are you sure you want to delete the extension '{{extensionId}}'?",
+            "delete-extension-text": "Be careful, after the confirmation the extension and all related data will become unrecoverable.",
+            "delete-extensions-title": "Are you sure you want to delete { count, select, 1 {1 extension} other {# extensions} }?",
+            "delete-extensions-text": "Be careful, after the confirmation all selected extensions will be removed.",
+            "converters": "Converters",
+            "converter-id": "Converter id",
+            "configuration": "Configuration",
+            "converter-configurations": "Converter configurations",
+            "token": "Security token",
+            "add-converter": "Add converter",
+            "add-config": "Add converter configuration",
+            "device-name-expression": "Device name expression",
+            "device-type-expression": "Device type expression",
+            "custom": "Custom",
+            "to-double": "To Double",
+            "transformer": "Transformer",
+            "json-required": "Transformer json is required.",
+            "json-parse": "Unable to parse transformer json.",
+            "attributes": "Attributes",
+            "add-attribute": "Add attribute",
+            "add-map": "Add mapping element",
+            "timeseries": "Timeseries",
+            "add-timeseries": "Add timeseries",
+            "field-required": "Field is required",
+            "brokers": "Brokers",
+            "add-broker": "Add broker",
+            "host": "Host",
+            "port": "Port",
+            "port-range": "Port should be in a range from 1 to 65535.",
+            "ssl": "Ssl",
+            "credentials": "Credentials",
+            "username": "Username",
+            "password": "Password",
+            "retry-interval": "Retry interval in milliseconds",
+            "anonymous": "Anonymous",
+            "basic": "Basic",
+            "pem": "PEM",
+            "ca-cert": "CA certificate file *",
+            "private-key": "Private key file *",
+            "cert": "Certificate file *",
+            "no-file": "No file selected.",
+            "drop-file": "Drop a file or click to select a file to upload.",
+            "mapping": "Mapping",
+            "topic-filter": "Topic filter",
+            "converter-type": "Converter type",
+            "converter-json": "Json",
+            "json-name-expression": "Device name json expression",
+            "topic-name-expression": "Device name topic expression",
+            "json-type-expression": "Device type json expression",
+            "topic-type-expression": "Device type topic expression",
+            "attribute-key-expression": "Attribute key expression",
+            "attr-json-key-expression": "Attribute key json expression",
+            "attr-topic-key-expression": "Attribute key topic expression",
+            "request-id-expression": "Request id expression",
+            "request-id-json-expression": "Request id json expression",
+            "request-id-topic-expression": "Request id topic expression",
+            "response-topic-expression": "Response topic expression",
+            "value-expression": "Value expression",
+            "topic": "Topic",
+            "timeout": "Timeout in milliseconds",
+            "converter-json-required": "Converter json is required.",
+            "converter-json-parse": "Unable to parse converter json.",
+            "filter-expression": "Filter expression",
+            "connect-requests": "Connect requests",
+            "add-connect-request": "Add connect request",
+            "disconnect-requests": "Disconnect requests",
+            "add-disconnect-request": "Add disconnect request",
+            "attribute-requests": "Attribute requests",
+            "add-attribute-request": "Add attribute request",
+            "attribute-updates": "Attribute updates",
+            "add-attribute-update": "Add attribute update",
+            "server-side-rpc": "Server side RPC",
+            "add-server-side-rpc-request": "Add server-side RPC request",
+            "device-name-filter": "Device name filter",
+            "attribute-filter": "Attribute filter",
+            "method-filter": "Method filter",
+            "request-topic-expression": "Request topic expression",
+            "response-timeout": "Response timeout in milliseconds",
+            "topic-expression": "Topic expression",
+            "client-scope": "Client scope",
+            "add-device": "Add device",
+            "opc-server": "Servers",
+            "opc-add-server": "Add server",
+            "opc-add-server-prompt": "Please add server",
+            "opc-application-name": "Application name",
+            "opc-application-uri": "Application uri",
+            "opc-scan-period-in-seconds": "Scan period in seconds",
+            "opc-security": "Security",
+            "opc-identity": "Identity",
+            "opc-keystore": "Keystore",
+            "opc-type": "Type",
+            "opc-keystore-type": "Type",
+            "opc-keystore-location": "Location *",
+            "opc-keystore-password": "Password",
+            "opc-keystore-alias": "Alias",
+            "opc-keystore-key-password": "Key password",
+            "opc-device-node-pattern": "Device node pattern",
+            "opc-device-name-pattern": "Device name pattern",
+            "modbus-server": "Servers/slaves",
+            "modbus-add-server": "Add server/slave",
+            "modbus-add-server-prompt": "Please add server/slave",
+            "modbus-transport": "Transport",
+            "modbus-port-name": "Serial port name",
+            "modbus-encoding": "Encoding",
+            "modbus-parity": "Parity",
+            "modbus-baudrate": "Baud rate",
+            "modbus-databits": "Data bits",
+            "modbus-stopbits": "Stop bits",
+            "modbus-databits-range": "Data bits should be in a range from 7 to 8.",
+            "modbus-stopbits-range": "Stop bits should be in a range from 1 to 2.",
+            "modbus-unit-id": "Unit ID",
+            "modbus-unit-id-range": "Unit ID should be in a range from 1 to 247.",
+            "modbus-device-name": "Device name",
+            "modbus-poll-period": "Poll period (ms)",
+            "modbus-attributes-poll-period": "Attributes poll period (ms)",
+            "modbus-timeseries-poll-period": "Timeseries poll period (ms)",
+            "modbus-poll-period-range": "Poll period should be positive value.",
+            "modbus-tag": "Tag",
+            "modbus-function": "Function",
+            "modbus-register-address": "Register address",
+            "modbus-register-address-range": "Register address should be in a range from 0 to 65535.",
+            "modbus-register-bit-index": "Bit index",
+            "modbus-register-bit-index-range": "Bit index should be in a range from 0 to 15.",
+            "modbus-register-count": "Register count",
+            "modbus-register-count-range": "Register count should be a positive value.",
+            "modbus-byte-order": "Byte order",
+
+            "sync": {
+                "status": "Status",
+                "sync": "Sync",
+                "not-sync": "Not sync",
+                "last-sync-time": "Last sync time",
+                "not-available": "Not available"
+            },
+
+            "export-extensions-configuration": "Export extensions configuration",
+            "import-extensions-configuration": "Import extensions configuration",
+            "import-extensions": "Import extensions",
+            "import-extension": "Import extension",
+            "export-extension": "Export extension",
+            "file": "Extensions file",
+            "invalid-file-error": "Invalid extension file"
+        },
         "fullscreen": {
             "expand": "展开到全屏",
             "exit": "退出全屏",
@@ -798,6 +960,13 @@ export default function addLocaleChinese(locales) {
             "no-return-error": "函数必须返回值!",
             "return-type-mismatch": "函数必须返回 '{{type}}' 类型的值!"
         },
+        "key-val": { // TODO
+            "key": "Key",
+            "value": "Value",
+            "remove-entry": "Remove entry",
+            "add-entry": "Add entry",
+            "no-data": "No entries"
+        },
         "layout": {
             "layout": "布局",
             "manage": "布局管理",
@@ -836,47 +1005,6 @@ export default function addLocaleChinese(locales) {
             "password-link-sent-message": "密码重置链接已成功发送!",
             "email": "电子邮件"
         },
-        "plugin": {
-            "plugins": "插件",
-            "delete": "删除插件",
-            "activate": "激活插件",
-            "suspend": "暂停插件",
-            "active": "激活",
-            "suspended": "暂停",
-            "name": "名称",
-            "name-required": "名称必填。",
-            "description": "描述",
-            "add": "添加插件",
-            "delete-plugin-title": "你确定要删除插件 '{{pluginName}}' 吗?",
-            "delete-plugin-text": "小心!确认后,插件和所有相关数据将不可恢复。",
-            "delete-plugins-title": "你确定你要删除 { count, select, 1 {1 插件} other {# 插件} } 吗?",
-            "delete-plugins-action-title": "删除 { count, select, 1 {1 插件} other {# 插件} }",
-            "delete-plugins-text": "小心!确认后,所有选定的插件将被删除,所有相关数据将不可恢复。",
-            "add-plugin-text": "添加新的插件",
-            "no-plugins-text": "没有找到插件",
-            "plugin-details": "插件详细信息",
-            "api-token": "API令牌",
-            "api-token-required": "API令牌必填。",
-            "type": "插件类型",
-            "type-required": "插件类型必填。",
-            "configuration": "插件配置",
-            "system": "系统",
-            "select-plugin": "选择插件",
-            "plugin": "插件",
-            "no-plugins-matching": "没有找到匹配'{{entity}}'的插件。",
-            "plugin-required": "插件必填。",
-            "plugin-require-match": "请选择一个现有的插件。",
-            "events": "事件",
-            "details": "详情",
-            "import": "导入插件",
-            "export": "导出插件",
-            "export-failed-error": "无法导出插件:{{error}}",
-            "create-new-plugin": "创建新的插件",
-            "plugin-file": "插件文件",
-            "invalid-plugin-file-error": "无法导入插件:插件数据结构无效。",
-            "copyId": "复制插件ID",
-            "idCopiedMessage": "插件ID已经复制到粘贴板"
-        },
         "position": {
             "top": "顶部",
             "bottom": "底部",
@@ -930,66 +1058,97 @@ export default function addLocaleChinese(locales) {
             "additional-info": "附加信息 (JSON)",
             "invalid-additional-info": "无法解析附加信息json。"
         },
-        "rule": {
-            "rule": "规则",
-            "rules": "规则",
-            "delete": "删除规则",
-            "activate": "激活规则",
-            "suspend": "暂停规则",
-            "active": "激活",
-            "suspended": "暂停",
-            "name": "名称",
-            "name-required": "名称必填。",
-            "description": "描述",
-            "add": "添加规则",
-            "delete-rule-title": "您确定要删除规则'{{ruleName}}'吗?",
-            "delete-rule-text": "小心!确认后,规则和所有相关数据将不可恢复。",
-            "delete-rules-title": "你确定要删除 {count, select, 1 {1 规则} other {# 规则}} 吗?",
-            "delete-rules-action-title": "删除 { count, select, 1 {1 规则} other {# 规则} }",
-            "delete-rules-text": "小心!确认后,所有选定的规则将被删除,所有相关数据将不可恢复。",
-            "add-rule-text": "添加新规则",
-            "no-rules-text": "没有找到规则",
-            "rule-details": "规则详情",
-            "filters": "过滤器",
-            "filter": "过滤器",
-            "add-filter-prompt": "请添加过滤器",
-            "remove-filter": "删除过滤器",
-            "add-filter": "添加过滤器",
-            "filter-name": "过滤器名称",
-            "filter-type": "过滤器类型",
-            "edit-filter": "编辑过滤器",
-            "view-filter": "查看过滤器",
-            "component-name": "名称",
-            "component-name-required": "名称必填。",
-            "component-type": "类型",
-            "component-type-required": "类型必填。",
-            "processor": "处理器",
-            "no-processor-configured": "未配置处理器",
-            "create-processor": "创建处理器",
-            "processor-name": "处理器名称",
-            "processor-type": "处理器类型",
-            "plugin-action": "插件动作",
-            "action-name": "动作名称",
-            "action-type": "动作类型",
-            "create-action-prompt": "请创建动作",
-            "create-action": "创建动作",
-            "details": "详情",
-            "events": "事件",
-            "system": "系统",
-            "import": "导入规则",
-            "export": "导出规则",
-            "export-failed-error": "无法导出规则:{{error}}",
-            "create-new-rule": "创建新规则",
-            "rule-file": "规则文件",
-            "invalid-rule-file-error": "无法导入规则:规则数据结构无效。",
-            "copyId": "Copy rule Id",
-            "idCopiedMessage": "规则ID已经复制到粘贴板",
-            "select-rule": "选择规则",
-            "no-rules-matching": "没有找到符合 '{{entity}}' 的规则。",
-            "rule-required": "规则必填"
+        "rulechain": { // TODO
+            "rulechain": "Rule chain",
+            "rulechains": "Rule chains",
+            "root": "Root",
+            "delete": "Delete rule chain",
+            "name": "Name",
+            "name-required": "Name is required.",
+            "description": "Description",
+            "add": "Add Rule Chain",
+            "set-root": "Make rule chain root",
+            "set-root-rulechain-title": "Are you sure you want to make the rule chain '{{ruleChainName}}' root?",
+            "set-root-rulechain-text": "After the confirmation the rule chain will become root and will handle all incoming transport messages.",
+            "delete-rulechain-title": "Are you sure you want to delete the rule chain '{{ruleChainName}}'?",
+            "delete-rulechain-text": "Be careful, after the confirmation the rule chain and all related data will become unrecoverable.",
+            "delete-rulechains-title": "Are you sure you want to delete { count, select, 1 {1 rule chain} other {# rule chains} }?",
+            "delete-rulechains-action-title": "Delete { count, select, 1 {1 rule chain} other {# rule chains} }",
+            "delete-rulechains-text": "Be careful, after the confirmation all selected rule chains will be removed and all related data will become unrecoverable.",
+            "add-rulechain-text": "Add new rule chain",
+            "no-rulechains-text": "No rule chains found",
+            "rulechain-details": "Rule chain details",
+            "details": "Details",
+            "events": "Events",
+            "system": "System",
+            "import": "Import rule chain",
+            "export": "Export rule chain",
+            "export-failed-error": "Unable to export rule chain: {{error}}",
+            "create-new-rulechain": "Create new rule chain",
+            "rulechain-file": "Rule chain file",
+            "invalid-rulechain-file-error": "Unable to import rule chain: Invalid rule chain data structure.",
+            "copyId": "Copy rule chain Id",
+            "idCopiedMessage": "Rule chain Id has been copied to clipboard",
+            "select-rulechain": "Select rule chain",
+            "no-rulechains-matching": "No rule chains matching '{{entity}}' were found.",
+            "rulechain-required": "Rule chain is required",
+            "management": "Rules management",
+            "debug-mode": "Debug mode"
         },
-        "rule-plugin": {
-            "management": "规则和插件管理"
+        "rulenode": { // TODO
+            "details": "Details",
+            "events": "Events",
+            "search": "Search nodes",
+            "open-node-library": "Open node library",
+            "add": "Add rule node",
+            "name": "Name",
+            "name-required": "Name is required.",
+            "type": "Type",
+            "description": "Description",
+            "delete": "Delete rule node",
+            "select-all-objects": "Select all nodes and connections",
+            "deselect-all-objects": "Deselect all nodes and connections",
+            "delete-selected-objects": "Delete selected nodes and connections",
+            "delete-selected": "Delete selected",
+            "select-all": "Select all",
+            "copy-selected": "Copy selected",
+            "deselect-all": "Deselect all",
+            "rulenode-details": "Rule node details",
+            "debug-mode": "Debug mode",
+            "configuration": "Configuration",
+            "link": "Link",
+            "link-details": "Rule node link details",
+            "add-link": "Add link",
+            "link-label": "Link label",
+            "link-label-required": "Link label is required.",
+            "custom-link-label": "Custom link label",
+            "custom-link-label-required": "Custom link label is required.",
+            "type-filter": "Filter",
+            "type-filter-details": "Filter incoming messages with configured conditions",
+            "type-enrichment": "Enrichment",
+            "type-enrichment-details": "Add additional information into Message Metadata",
+            "type-transformation": "Transformation",
+            "type-transformation-details": "Change Message payload and Metadata",
+            "type-action": "Action",
+            "type-action-details": "Perform special action",
+            "type-external": "External",
+            "type-external-details": "Interacts with external system",
+            "type-rule-chain": "Rule Chain",
+            "type-rule-chain-details": "Forwards incoming messages to specified Rule Chain",
+            "type-input": "Input",
+            "type-input-details": "Logical input of Rule Chain, forwards incoming messages to next related Rule Node",
+            "directive-is-not-loaded": "Defined configuration directive '{{directiveName}}' is not available.",
+            "ui-resources-load-error": "Failed to load configuration ui resources.",
+            "invalid-target-rulechain": "Unable to resolve target rule chain!",
+            "test-script-function": "Test script function",
+            "message": "Message",
+            "message-type": "Message type",
+            "message-type-required": "Message type is required",
+            "metadata": "Metadata",
+            "metadata-required": "Metadata entries can't be empty.",
+            "output": "Output",
+            "test": "Test",
+            "help": "Help"
         },
         "tenant": {
             "tenant": "租户",
@@ -1081,7 +1240,8 @@ export default function addLocaleChinese(locales) {
             "activation-link": "用户激活链接",
             "activation-link-text": "使用该链接 <a href='{{activationLink}}' target='_blank'>激活</a> 激活用户:",
             "copy-activation-link": "复制用户激活链接",
-            "activation-link-copied-message": "用户激活链接已经复制到粘贴板"
+            "activation-link-copied-message": "用户激活链接已经复制到粘贴板",
+            "details": "Details" // TODO
         },
         "value": {
             "type": "值类型",
@@ -1248,6 +1408,14 @@ export default function addLocaleChinese(locales) {
             "material-icons": "素材图标",
             "show-all": "显示所有图标"
         },
+        "custom": { // TODO
+            "widget-action": {
+                "action-cell-button": "Action cell button",
+                "row-click": "On row click",
+                "marker-click": "On marker click",
+                "tooltip-tag-action": "Tooltip tag action"
+            }
+        },
         "language": {
             "language": "语言",
             "en_US": "英语",