thingsboard-aplcache

Details

diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TsKvEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TsKvEntity.java
index a6d3ea6..fe10286 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TsKvEntity.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TsKvEntity.java
@@ -49,25 +49,53 @@ import static org.thingsboard.server.dao.model.ModelConstants.TS_COLUMN;
 @IdClass(TsKvCompositeKey.class)
 public final class TsKvEntity implements ToData<TsKvEntry> {
 
-    public TsKvEntity() {
-    }
+    private static final String SUM = "SUM";
+    private static final String AVG = "AVG";
+    private static final String MIN = "MIN";
+    private static final String MAX = "MAX";
 
-    public TsKvEntity(Double avgLongValue, Double avgDoubleValue) {
-        if(avgLongValue != null) {
-            this.longValue = avgLongValue.longValue();
-        }
-        this.doubleValue = avgDoubleValue;
+    public TsKvEntity() {
     }
 
-    public TsKvEntity(Long sumLongValue, Double sumDoubleValue) {
-        this.longValue = sumLongValue;
-        this.doubleValue = sumDoubleValue;
+    public TsKvEntity(String strValue) {
+        this.strValue = strValue;
     }
 
-    public TsKvEntity(String strValue, Long longValue, Double doubleValue) {
-        this.strValue = strValue;
-        this.longValue = longValue;
-        this.doubleValue = doubleValue;
+    public TsKvEntity(Long longValue, Double doubleValue, Long longCountValue, Long doubleCountValue, String aggType) {
+        switch (aggType) {
+            case AVG:
+                double sum = 0.0;
+                if (longValue != null) {
+                    sum += longValue;
+                }
+                if (doubleValue != null) {
+                    sum += doubleValue;
+                }
+                long totalCount = longCountValue + doubleCountValue;
+                if (totalCount > 0) {
+                    this.doubleValue = sum / (longCountValue + doubleCountValue);
+                } else {
+                    this.doubleValue = 0.0;
+                }
+                break;
+            case SUM:
+                if (doubleCountValue > 0) {
+                    this.doubleValue = doubleValue + (longValue != null ? longValue.doubleValue() : 0.0);
+                } else {
+                    this.longValue = longValue;
+                }
+                break;
+            case MIN:
+            case MAX:
+                if (longCountValue > 0 && doubleCountValue > 0) {
+                    this.doubleValue = MAX.equals(aggType) ? Math.max(doubleValue, longValue.doubleValue()) : Math.min(doubleValue, longValue.doubleValue());
+                } else if (doubleCountValue > 0) {
+                    this.doubleValue = doubleValue;
+                } else if (longCountValue > 0) {
+                    this.longValue = longValue;
+                }
+                break;
+        }
     }
 
     public TsKvEntity(Long booleanValueCount, Long strValueCount, Long longValueCount, Long doubleValueCount) {
@@ -75,10 +103,8 @@ public final class TsKvEntity implements ToData<TsKvEntry> {
             this.longValue = booleanValueCount;
         } else if (strValueCount != 0) {
             this.longValue = strValueCount;
-        } else if (longValueCount != 0) {
-            this.longValue = longValueCount;
-        } else if (doubleValueCount != 0) {
-            this.longValue = doubleValueCount;
+        } else {
+            this.longValue = longValueCount + doubleValueCount;
         }
     }
 
diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/JpaTimeseriesDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/JpaTimeseriesDao.java
index 3120148..a04944e 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/JpaTimeseriesDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/JpaTimeseriesDao.java
@@ -161,52 +161,62 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp
     }
 
     private ListenableFuture<Optional<TsKvEntry>> findAndAggregateAsync(EntityId entityId, String key, long startTs, long endTs, long ts, Aggregation aggregation) {
-        CompletableFuture<TsKvEntity> entity;
+        List<CompletableFuture<TsKvEntity>> entitiesFutures = new ArrayList<>();
         String entityIdStr = fromTimeUUID(entityId.getId());
         switch (aggregation) {
             case AVG:
-                entity = tsKvRepository.findAvg(
+                entitiesFutures.add(tsKvRepository.findAvg(
                         entityIdStr,
                         entityId.getEntityType(),
                         key,
                         startTs,
-                        endTs);
+                        endTs));
 
                 break;
             case MAX:
-                entity = tsKvRepository.findMax(
+                entitiesFutures.add(tsKvRepository.findStringMax(
                         entityIdStr,
                         entityId.getEntityType(),
                         key,
                         startTs,
-                        endTs);
+                        endTs));
+                entitiesFutures.add(tsKvRepository.findNumericMax(
+                        entityIdStr,
+                        entityId.getEntityType(),
+                        key,
+                        startTs,
+                        endTs));
 
                 break;
             case MIN:
-                entity = tsKvRepository.findMin(
+                entitiesFutures.add(tsKvRepository.findStringMin(
                         entityIdStr,
                         entityId.getEntityType(),
                         key,
                         startTs,
-                        endTs);
-
+                        endTs));
+                entitiesFutures.add(tsKvRepository.findNumericMin(
+                        entityIdStr,
+                        entityId.getEntityType(),
+                        key,
+                        startTs,
+                        endTs));
                 break;
             case SUM:
-                entity = tsKvRepository.findSum(
+                entitiesFutures.add(tsKvRepository.findSum(
                         entityIdStr,
                         entityId.getEntityType(),
                         key,
                         startTs,
-                        endTs);
-
+                        endTs));
                 break;
             case COUNT:
-                entity = tsKvRepository.findCount(
+                entitiesFutures.add(tsKvRepository.findCount(
                         entityIdStr,
                         entityId.getEntityType(),
                         key,
                         startTs,
-                        endTs);
+                        endTs));
 
                 break;
             default:
@@ -214,11 +224,27 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp
         }
 
         SettableFuture<TsKvEntity> listenableFuture = SettableFuture.create();
-        entity.whenComplete((tsKvEntity, throwable) -> {
+
+
+        CompletableFuture<List<TsKvEntity>> entities =
+                CompletableFuture.allOf(entitiesFutures.toArray(new CompletableFuture[entitiesFutures.size()]))
+                .thenApply(v -> entitiesFutures.stream()
+                        .map(CompletableFuture::join)
+                        .collect(Collectors.toList()));
+
+
+        entities.whenComplete((tsKvEntities, throwable) -> {
             if (throwable != null) {
                 listenableFuture.setException(throwable);
             } else {
-                listenableFuture.set(tsKvEntity);
+                TsKvEntity result = null;
+                for (TsKvEntity entity : tsKvEntities) {
+                    if (entity.isNotEmpty()) {
+                        result = entity;
+                        break;
+                    }
+                }
+                listenableFuture.set(result);
             }
         });
         return Futures.transform(listenableFuture, new Function<TsKvEntity, Optional<TsKvEntry>>() {
diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/TsKvRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/TsKvRepository.java
index 296d173..79aa71b 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/TsKvRepository.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/TsKvRepository.java
@@ -35,7 +35,7 @@ public interface TsKvRepository extends CrudRepository<TsKvEntity, TsKvComposite
 
     @Query("SELECT tskv FROM TsKvEntity tskv WHERE tskv.entityId = :entityId " +
             "AND tskv.entityType = :entityType AND tskv.key = :entityKey " +
-            "AND tskv.ts > :startTs AND tskv.ts < :endTs")
+            "AND tskv.ts > :startTs AND tskv.ts <= :endTs")
     List<TsKvEntity> findAllWithLimit(@Param("entityId") String entityId,
                                       @Param("entityType") EntityType entityType,
                                       @Param("entityKey") String key,
@@ -55,29 +55,63 @@ public interface TsKvRepository extends CrudRepository<TsKvEntity, TsKvComposite
                 @Param("endTs") long endTs);
 
     @Async
-    @Query("SELECT new TsKvEntity(MAX(tskv.strValue), MAX(tskv.longValue), MAX(tskv.doubleValue)) FROM TsKvEntity tskv " +
+    @Query("SELECT new TsKvEntity(MAX(tskv.strValue)) FROM TsKvEntity tskv " +
+            "WHERE tskv.strValue IS NOT NULL " +
+            "AND tskv.entityId = :entityId AND tskv.entityType = :entityType " +
+            "AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts <= :endTs")
+    CompletableFuture<TsKvEntity> findStringMax(@Param("entityId") String entityId,
+                                                @Param("entityType") EntityType entityType,
+                                                @Param("entityKey") String entityKey,
+                                                @Param("startTs") long startTs,
+                                                @Param("endTs") long endTs);
+
+    @Async
+    @Query("SELECT new TsKvEntity(MAX(COALESCE(tskv.longValue, -9223372036854775807)), " +
+            "MAX(COALESCE(tskv.doubleValue, -1.79769E+308)), " +
+            "SUM(CASE WHEN tskv.longValue IS NULL THEN 0 ELSE 1 END), " +
+            "SUM(CASE WHEN tskv.doubleValue IS NULL THEN 0 ELSE 1 END), " +
+            "'MAX') FROM TsKvEntity tskv " +
             "WHERE tskv.entityId = :entityId AND tskv.entityType = :entityType " +
-            "AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts < :endTs")
-    CompletableFuture<TsKvEntity> findMax(@Param("entityId") String entityId,
+            "AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts <= :endTs")
+    CompletableFuture<TsKvEntity> findNumericMax(@Param("entityId") String entityId,
+                                          @Param("entityType") EntityType entityType,
+                                          @Param("entityKey") String entityKey,
+                                          @Param("startTs") long startTs,
+                                          @Param("endTs") long endTs);
+
+
+    @Async
+    @Query("SELECT new TsKvEntity(MIN(tskv.strValue)) FROM TsKvEntity tskv " +
+            "WHERE tskv.strValue IS NOT NULL " +
+            "AND tskv.entityId = :entityId AND tskv.entityType = :entityType " +
+            "AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts <= :endTs")
+    CompletableFuture<TsKvEntity> findStringMin(@Param("entityId") String entityId,
                                           @Param("entityType") EntityType entityType,
                                           @Param("entityKey") String entityKey,
                                           @Param("startTs") long startTs,
                                           @Param("endTs") long endTs);
 
     @Async
-    @Query("SELECT new TsKvEntity(MIN(tskv.strValue), MIN(tskv.longValue), MIN(tskv.doubleValue)) FROM TsKvEntity tskv " +
+    @Query("SELECT new TsKvEntity(MIN(COALESCE(tskv.longValue, 9223372036854775807)), " +
+            "MIN(COALESCE(tskv.doubleValue, 1.79769E+308)), " +
+            "SUM(CASE WHEN tskv.longValue IS NULL THEN 0 ELSE 1 END), " +
+            "SUM(CASE WHEN tskv.doubleValue IS NULL THEN 0 ELSE 1 END), " +
+            "'MIN') FROM TsKvEntity tskv " +
             "WHERE tskv.entityId = :entityId AND tskv.entityType = :entityType " +
-            "AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts < :endTs")
-    CompletableFuture<TsKvEntity> findMin(@Param("entityId") String entityId,
+            "AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts <= :endTs")
+    CompletableFuture<TsKvEntity> findNumericMin(@Param("entityId") String entityId,
                                           @Param("entityType") EntityType entityType,
                                           @Param("entityKey") String entityKey,
                                           @Param("startTs") long startTs,
                                           @Param("endTs") long endTs);
 
     @Async
-    @Query("SELECT new TsKvEntity(COUNT(tskv.booleanValue), COUNT(tskv.strValue), COUNT(tskv.longValue), COUNT(tskv.doubleValue)) FROM TsKvEntity tskv " +
+    @Query("SELECT new TsKvEntity(SUM(CASE WHEN tskv.booleanValue IS NULL THEN 0 ELSE 1 END), " +
+            "SUM(CASE WHEN tskv.strValue IS NULL THEN 0 ELSE 1 END), " +
+            "SUM(CASE WHEN tskv.longValue IS NULL THEN 0 ELSE 1 END), " +
+            "SUM(CASE WHEN tskv.doubleValue IS NULL THEN 0 ELSE 1 END)) FROM TsKvEntity tskv " +
             "WHERE tskv.entityId = :entityId AND tskv.entityType = :entityType " +
-            "AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts < :endTs")
+            "AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts <= :endTs")
     CompletableFuture<TsKvEntity> findCount(@Param("entityId") String entityId,
                                             @Param("entityType") EntityType entityType,
                                             @Param("entityKey") String entityKey,
@@ -85,23 +119,31 @@ public interface TsKvRepository extends CrudRepository<TsKvEntity, TsKvComposite
                                             @Param("endTs") long endTs);
 
     @Async
-    @Query("SELECT new TsKvEntity(AVG(tskv.longValue), AVG(tskv.doubleValue)) FROM TsKvEntity tskv " +
+    @Query("SELECT new TsKvEntity(SUM(COALESCE(tskv.longValue, 0)), " +
+            "SUM(COALESCE(tskv.doubleValue, 0.0)), " +
+            "SUM(CASE WHEN tskv.longValue IS NULL THEN 0 ELSE 1 END), " +
+            "SUM(CASE WHEN tskv.doubleValue IS NULL THEN 0 ELSE 1 END), " +
+            "'AVG') FROM TsKvEntity tskv " +
             "WHERE tskv.entityId = :entityId AND tskv.entityType = :entityType " +
-            "AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts < :endTs")
+            "AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts <= :endTs")
     CompletableFuture<TsKvEntity> findAvg(@Param("entityId") String entityId,
                                           @Param("entityType") EntityType entityType,
                                           @Param("entityKey") String entityKey,
                                           @Param("startTs") long startTs,
                                           @Param("endTs") long endTs);
 
-
     @Async
-    @Query("SELECT new TsKvEntity(SUM(tskv.longValue), SUM(tskv.doubleValue)) FROM TsKvEntity tskv " +
+    @Query("SELECT new TsKvEntity(SUM(COALESCE(tskv.longValue, 0)), " +
+            "SUM(COALESCE(tskv.doubleValue, 0.0)), " +
+            "SUM(CASE WHEN tskv.longValue IS NULL THEN 0 ELSE 1 END), " +
+            "SUM(CASE WHEN tskv.doubleValue IS NULL THEN 0 ELSE 1 END), " +
+            "'SUM') FROM TsKvEntity tskv " +
             "WHERE tskv.entityId = :entityId AND tskv.entityType = :entityType " +
-            "AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts < :endTs")
+            "AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts <= :endTs")
     CompletableFuture<TsKvEntity> findSum(@Param("entityId") String entityId,
                                           @Param("entityType") EntityType entityType,
                                           @Param("entityKey") String entityKey,
                                           @Param("startTs") long startTs,
                                           @Param("endTs") long endTs);
+
 }
diff --git a/dao/src/main/java/org/thingsboard/server/dao/timeseries/AggregatePartitionsFunction.java b/dao/src/main/java/org/thingsboard/server/dao/timeseries/AggregatePartitionsFunction.java
index ac5ee64..ea1e8f1 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/timeseries/AggregatePartitionsFunction.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/timeseries/AggregatePartitionsFunction.java
@@ -79,7 +79,7 @@ public class AggregatePartitionsFunction implements com.google.common.base.Funct
     }
 
     private void processResultSetRow(Row row, AggregationResult aggResult) {
-        long curCount;
+        long curCount = 0L;
 
         Long curLValue = null;
         Double curDValue = null;
@@ -91,14 +91,18 @@ public class AggregatePartitionsFunction implements com.google.common.base.Funct
         long boolCount = row.getLong(BOOL_CNT_POS);
         long strCount = row.getLong(STR_CNT_POS);
 
-        if (longCount > 0) {
-            aggResult.dataType = DataType.LONG;
-            curCount = longCount;
-            curLValue = getLongValue(row);
-        } else if (doubleCount > 0) {
-            aggResult.dataType = DataType.DOUBLE;
-            curCount = doubleCount;
-            curDValue = getDoubleValue(row);
+        if (longCount > 0 || doubleCount > 0) {
+            if (longCount > 0) {
+                aggResult.dataType = DataType.LONG;
+                curCount += longCount;
+                curLValue = getLongValue(row);
+            }
+            if (doubleCount > 0) {
+                aggResult.hasDouble = true;
+                aggResult.dataType = DataType.DOUBLE;
+                curCount += doubleCount;
+                curDValue = getDoubleValue(row);
+            }
         } else if (boolCount > 0) {
             aggResult.dataType = DataType.BOOLEAN;
             curCount = boolCount;
@@ -126,16 +130,20 @@ public class AggregatePartitionsFunction implements com.google.common.base.Funct
         aggResult.count += curCount;
         if (curDValue != null) {
             aggResult.dValue = aggResult.dValue == null ? curDValue : aggResult.dValue + curDValue;
-        } else if (curLValue != null) {
+        }
+        if (curLValue != null) {
             aggResult.lValue = aggResult.lValue == null ? curLValue : aggResult.lValue + curLValue;
         }
     }
 
     private void processMinAggregation(AggregationResult aggResult, Long curLValue, Double curDValue, Boolean curBValue, String curSValue) {
-        if (curDValue != null) {
-            aggResult.dValue = aggResult.dValue == null ? curDValue : Math.min(aggResult.dValue, curDValue);
-        } else if (curLValue != null) {
-            aggResult.lValue = aggResult.lValue == null ? curLValue : Math.min(aggResult.lValue, curLValue);
+        if (curDValue != null || curLValue != null) {
+            if (curDValue != null) {
+                aggResult.dValue = aggResult.dValue == null ? curDValue : Math.min(aggResult.dValue, curDValue);
+            }
+            if (curLValue != null) {
+                aggResult.lValue = aggResult.lValue == null ? curLValue : Math.min(aggResult.lValue, curLValue);
+            }
         } else if (curBValue != null) {
             aggResult.bValue = aggResult.bValue == null ? curBValue : aggResult.bValue && curBValue;
         } else if (curSValue != null && (aggResult.sValue == null || curSValue.compareTo(aggResult.sValue) < 0)) {
@@ -144,10 +152,13 @@ public class AggregatePartitionsFunction implements com.google.common.base.Funct
     }
 
     private void processMaxAggregation(AggregationResult aggResult, Long curLValue, Double curDValue, Boolean curBValue, String curSValue) {
-        if (curDValue != null) {
-            aggResult.dValue = aggResult.dValue == null ? curDValue : Math.max(aggResult.dValue, curDValue);
-        } else if (curLValue != null) {
-            aggResult.lValue = aggResult.lValue == null ? curLValue : Math.max(aggResult.lValue, curLValue);
+        if (curDValue != null || curLValue != null) {
+            if (curDValue != null) {
+                aggResult.dValue = aggResult.dValue == null ? curDValue : Math.max(aggResult.dValue, curDValue);
+            }
+            if (curLValue != null) {
+                aggResult.lValue = aggResult.lValue == null ? curLValue : Math.max(aggResult.lValue, curLValue);
+            }
         } else if (curBValue != null) {
             aggResult.bValue = aggResult.bValue == null ? curBValue : aggResult.bValue || curBValue;
         } else if (curSValue != null && (aggResult.sValue == null || curSValue.compareTo(aggResult.sValue) > 0)) {
@@ -211,20 +222,27 @@ public class AggregatePartitionsFunction implements com.google.common.base.Funct
     private Optional<TsKvEntry> processAvgOrSumResult(AggregationResult aggResult) {
         if (aggResult.count == 0 || (aggResult.dataType == DataType.DOUBLE && aggResult.dValue == null) || (aggResult.dataType == DataType.LONG && aggResult.lValue == null)) {
             return Optional.empty();
-        } else if (aggResult.dataType == DataType.DOUBLE) {
-            return Optional.of(new BasicTsKvEntry(ts, new DoubleDataEntry(key, aggregation == Aggregation.SUM ? aggResult.dValue : (aggResult.dValue / aggResult.count))));
-        } else if (aggResult.dataType == DataType.LONG) {
-            return Optional.of(new BasicTsKvEntry(ts, new LongDataEntry(key, aggregation == Aggregation.SUM ? aggResult.lValue : (aggResult.lValue / aggResult.count))));
+        } else if (aggResult.dataType == DataType.DOUBLE || aggResult.dataType == DataType.LONG) {
+            if(aggregation == Aggregation.AVG || aggResult.hasDouble) {
+                double sum = Optional.ofNullable(aggResult.dValue).orElse(0.0d) + Optional.ofNullable(aggResult.lValue).orElse(0L);
+                return Optional.of(new BasicTsKvEntry(ts, new DoubleDataEntry(key, aggregation == Aggregation.SUM ? sum : (sum / aggResult.count))));
+            } else {
+                return Optional.of(new BasicTsKvEntry(ts, new LongDataEntry(key, aggregation == Aggregation.SUM ? aggResult.lValue : (aggResult.lValue / aggResult.count))));
+            }
         }
         return Optional.empty();
     }
 
     private Optional<TsKvEntry> processMinOrMaxResult(AggregationResult aggResult) {
-        if (aggResult.dataType == DataType.DOUBLE) {
-            return Optional.of(new BasicTsKvEntry(ts, new DoubleDataEntry(key, aggResult.dValue)));
-        } else if (aggResult.dataType == DataType.LONG) {
-            return Optional.of(new BasicTsKvEntry(ts, new LongDataEntry(key, aggResult.lValue)));
-        } else if (aggResult.dataType == DataType.STRING) {
+        if (aggResult.dataType == DataType.DOUBLE || aggResult.dataType == DataType.LONG) {
+            if(aggResult.hasDouble) {
+                double currentD = aggregation == Aggregation.MIN ? Optional.ofNullable(aggResult.dValue).orElse(Double.MAX_VALUE) : Optional.ofNullable(aggResult.dValue).orElse(Double.MIN_VALUE);
+                double currentL = aggregation == Aggregation.MIN ? Optional.ofNullable(aggResult.lValue).orElse(Long.MAX_VALUE) : Optional.ofNullable(aggResult.lValue).orElse(Long.MIN_VALUE);
+                return Optional.of(new BasicTsKvEntry(ts, new DoubleDataEntry(key, aggregation == Aggregation.MIN ? Math.min(currentD, currentL) : Math.max(currentD, currentL))));
+            } else {
+                return Optional.of(new BasicTsKvEntry(ts, new LongDataEntry(key, aggResult.lValue)));
+            }
+        }  else if (aggResult.dataType == DataType.STRING) {
             return Optional.of(new BasicTsKvEntry(ts, new StringDataEntry(key, aggResult.sValue)));
         } else {
             return Optional.of(new BasicTsKvEntry(ts, new BooleanDataEntry(key, aggResult.bValue)));
@@ -238,5 +256,6 @@ public class AggregatePartitionsFunction implements com.google.common.base.Funct
         Double dValue = null;
         Long lValue = null;
         long count = 0;
+        boolean hasDouble = false;
     }
 }
diff --git a/dao/src/test/java/org/thingsboard/server/dao/NoSqlDaoServiceTestSuite.java b/dao/src/test/java/org/thingsboard/server/dao/NoSqlDaoServiceTestSuite.java
index 55c2f70..ddea70a 100644
--- a/dao/src/test/java/org/thingsboard/server/dao/NoSqlDaoServiceTestSuite.java
+++ b/dao/src/test/java/org/thingsboard/server/dao/NoSqlDaoServiceTestSuite.java
@@ -25,9 +25,7 @@ import java.util.Arrays;
 
 @RunWith(ClasspathSuite.class)
 @ClassnameFilters({
-        "org.thingsboard.server.dao.service.*ServiceNoSqlTest",
-        "org.thingsboard.server.dao.service.queue.cassandra.*.*.*Test",
-        "org.thingsboard.server.dao.service.queue.cassandra.*Test"
+        "org.thingsboard.server.dao.service.*ServiceNoSqlTest"
 })
 public class NoSqlDaoServiceTestSuite {
 
diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/timeseries/BaseTimeseriesServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/timeseries/BaseTimeseriesServiceTest.java
index b409dea..c2af9d6 100644
--- a/dao/src/test/java/org/thingsboard/server/dao/service/timeseries/BaseTimeseriesServiceTest.java
+++ b/dao/src/test/java/org/thingsboard/server/dao/service/timeseries/BaseTimeseriesServiceTest.java
@@ -17,10 +17,7 @@ package org.thingsboard.server.dao.service.timeseries;
 
 import com.datastax.driver.core.utils.UUIDs;
 import lombok.extern.slf4j.Slf4j;
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
+import org.junit.*;
 import org.thingsboard.server.common.data.EntityView;
 import org.thingsboard.server.common.data.Tenant;
 import org.thingsboard.server.common.data.id.DeviceId;
@@ -221,13 +218,13 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
                 60000, 20000, 3, Aggregation.AVG))).get();
         assertEquals(3, list.size());
         assertEquals(10000, list.get(0).getTs());
-        assertEquals(java.util.Optional.of(150L), list.get(0).getLongValue());
+        assertEquals(java.util.Optional.of(150.0), list.get(0).getDoubleValue());
 
         assertEquals(30000, list.get(1).getTs());
-        assertEquals(java.util.Optional.of(350L), list.get(1).getLongValue());
+        assertEquals(java.util.Optional.of(350.0), list.get(1).getDoubleValue());
 
         assertEquals(50000, list.get(2).getTs());
-        assertEquals(java.util.Optional.of(550L), list.get(2).getLongValue());
+        assertEquals(java.util.Optional.of(550.0), list.get(2).getDoubleValue());
 
         list = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0,
                 60000, 20000, 3, Aggregation.SUM))).get();
@@ -280,6 +277,157 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
 
         assertEquals(50000, list.get(2).getTs());
         assertEquals(java.util.Optional.of(2L), list.get(2).getLongValue());
+
+
+        entries.add(save(deviceId, 65000, "A1"));
+        entries.add(save(deviceId, 75000, "A2"));
+        entries.add(save(deviceId, 85000, "B1"));
+        entries.add(save(deviceId, 95000, "B2"));
+        entries.add(save(deviceId, 105000, "C1"));
+        entries.add(save(deviceId, 115000, "C2"));
+
+        list = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 60000,
+                120000, 20000, 3, Aggregation.NONE))).get();
+        assertEquals(3, list.size());
+        assertEquals(115000, list.get(0).getTs());
+        assertEquals(java.util.Optional.of("C2"), list.get(0).getStrValue());
+
+        assertEquals(105000, list.get(1).getTs());
+        assertEquals(java.util.Optional.of("C1"), list.get(1).getStrValue());
+
+        assertEquals(95000, list.get(2).getTs());
+        assertEquals(java.util.Optional.of("B2"), list.get(2).getStrValue());
+
+
+        list = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 60000,
+                120000, 20000, 3, Aggregation.MIN))).get();
+
+        assertEquals(3, list.size());
+        assertEquals(70000, list.get(0).getTs());
+        assertEquals(java.util.Optional.of("A1"), list.get(0).getStrValue());
+
+        assertEquals(90000, list.get(1).getTs());
+        assertEquals(java.util.Optional.of("B1"), list.get(1).getStrValue());
+
+        assertEquals(110000, list.get(2).getTs());
+        assertEquals(java.util.Optional.of("C1"), list.get(2).getStrValue());
+
+        list = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 60000,
+                120000, 20000, 3, Aggregation.MAX))).get();
+
+        assertEquals(3, list.size());
+        assertEquals(70000, list.get(0).getTs());
+        assertEquals(java.util.Optional.of("A2"), list.get(0).getStrValue());
+
+        assertEquals(90000, list.get(1).getTs());
+        assertEquals(java.util.Optional.of("B2"), list.get(1).getStrValue());
+
+        assertEquals(110000, list.get(2).getTs());
+        assertEquals(java.util.Optional.of("C2"), list.get(2).getStrValue());
+
+        list = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 60000,
+                120000, 20000, 3, Aggregation.COUNT))).get();
+
+        assertEquals(3, list.size());
+        assertEquals(70000, list.get(0).getTs());
+        assertEquals(java.util.Optional.of(2L), list.get(0).getLongValue());
+
+        assertEquals(90000, list.get(1).getTs());
+        assertEquals(java.util.Optional.of(2L), list.get(1).getLongValue());
+
+        assertEquals(110000, list.get(2).getTs());
+        assertEquals(java.util.Optional.of(2L), list.get(2).getLongValue());
+    }
+
+    @Test
+    public void testFindDeviceLongAndDoubleTsData() throws Exception {
+        DeviceId deviceId = new DeviceId(UUIDs.timeBased());
+        List<TsKvEntry> entries = new ArrayList<>();
+
+        entries.add(save(deviceId, 5000, 100));
+        entries.add(save(deviceId, 15000, 200.0));
+
+        entries.add(save(deviceId, 25000, 300));
+        entries.add(save(deviceId, 35000, 400.0));
+
+        entries.add(save(deviceId, 45000, 500));
+        entries.add(save(deviceId, 55000, 600.0));
+
+        List<TsKvEntry> list = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0,
+                60000, 20000, 3, Aggregation.NONE))).get();
+        assertEquals(3, list.size());
+        assertEquals(55000, list.get(0).getTs());
+        assertEquals(java.util.Optional.of(600.0), list.get(0).getDoubleValue());
+
+        assertEquals(45000, list.get(1).getTs());
+        assertEquals(java.util.Optional.of(500L), list.get(1).getLongValue());
+
+        assertEquals(35000, list.get(2).getTs());
+        assertEquals(java.util.Optional.of(400.0), list.get(2).getDoubleValue());
+
+        list = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0,
+                60000, 20000, 3, Aggregation.AVG))).get();
+        assertEquals(3, list.size());
+        assertEquals(10000, list.get(0).getTs());
+        assertEquals(java.util.Optional.of(150.0), list.get(0).getDoubleValue());
+
+        assertEquals(30000, list.get(1).getTs());
+        assertEquals(java.util.Optional.of(350.0), list.get(1).getDoubleValue());
+
+        assertEquals(50000, list.get(2).getTs());
+        assertEquals(java.util.Optional.of(550.0), list.get(2).getDoubleValue());
+
+        list = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0,
+                60000, 20000, 3, Aggregation.SUM))).get();
+
+        assertEquals(3, list.size());
+        assertEquals(10000, list.get(0).getTs());
+        assertEquals(java.util.Optional.of(300.0), list.get(0).getDoubleValue());
+
+        assertEquals(30000, list.get(1).getTs());
+        assertEquals(java.util.Optional.of(700.0), list.get(1).getDoubleValue());
+
+        assertEquals(50000, list.get(2).getTs());
+        assertEquals(java.util.Optional.of(1100.0), list.get(2).getDoubleValue());
+
+        list = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0,
+                60000, 20000, 3, Aggregation.MIN))).get();
+
+        assertEquals(3, list.size());
+        assertEquals(10000, list.get(0).getTs());
+        assertEquals(java.util.Optional.of(100.0), list.get(0).getDoubleValue());
+
+        assertEquals(30000, list.get(1).getTs());
+        assertEquals(java.util.Optional.of(300.0), list.get(1).getDoubleValue());
+
+        assertEquals(50000, list.get(2).getTs());
+        assertEquals(java.util.Optional.of(500.0), list.get(2).getDoubleValue());
+
+        list = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0,
+                60000, 20000, 3, Aggregation.MAX))).get();
+
+        assertEquals(3, list.size());
+        assertEquals(10000, list.get(0).getTs());
+        assertEquals(java.util.Optional.of(200.0), list.get(0).getDoubleValue());
+
+        assertEquals(30000, list.get(1).getTs());
+        assertEquals(java.util.Optional.of(400.0), list.get(1).getDoubleValue());
+
+        assertEquals(50000, list.get(2).getTs());
+        assertEquals(java.util.Optional.of(600.0), list.get(2).getDoubleValue());
+
+        list = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0,
+                60000, 20000, 3, Aggregation.COUNT))).get();
+
+        assertEquals(3, list.size());
+        assertEquals(10000, list.get(0).getTs());
+        assertEquals(java.util.Optional.of(2L), list.get(0).getLongValue());
+
+        assertEquals(30000, list.get(1).getTs());
+        assertEquals(java.util.Optional.of(2L), list.get(1).getLongValue());
+
+        assertEquals(50000, list.get(2).getTs());
+        assertEquals(java.util.Optional.of(2L), list.get(2).getLongValue());
     }
 
     private TsKvEntry save(DeviceId deviceId, long ts, long value) throws Exception {
@@ -288,6 +436,19 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
         return entry;
     }
 
+    private TsKvEntry save(DeviceId deviceId, long ts, double value) throws Exception {
+        TsKvEntry entry = new BasicTsKvEntry(ts, new DoubleDataEntry(LONG_KEY, value));
+        tsService.save(tenantId, deviceId, entry).get();
+        return entry;
+    }
+
+    private TsKvEntry save(DeviceId deviceId, long ts, String value) throws Exception {
+        TsKvEntry entry = new BasicTsKvEntry(ts, new StringDataEntry(LONG_KEY, value));
+        tsService.save(tenantId, deviceId, entry).get();
+        return entry;
+    }
+
+
     private void saveEntries(DeviceId deviceId, long ts) throws ExecutionException, InterruptedException {
         tsService.save(tenantId, deviceId, toTsEntry(ts, stringKvEntry)).get();
         tsService.save(tenantId, deviceId, toTsEntry(ts, longKvEntry)).get();

ui/package.json 6(+4 -2)

diff --git a/ui/package.json b/ui/package.json
index 20258ad..7345ae6 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -11,7 +11,7 @@
   ],
   "scripts": {
     "start": "babel-node --max_old_space_size=4096 server.js",
-    "build": "cross-env NODE_ENV=production webpack -p"
+    "build": "cross-env NODE_ENV=production webpack"
   },
   "dependencies": {
     "@flowjs/ng-flow": "^2.7.1",
@@ -136,7 +136,9 @@
     "webpack-dev-middleware": "^1.6.1",
     "webpack-dev-server": "^1.15.1",
     "webpack-hot-middleware": "^2.12.2",
-    "webpack-material-design-icons": "^0.1.0"
+    "webpack-material-design-icons": "^0.1.0",
+    "uglifyjs-webpack-plugin": "^1.3.0",
+    "happypack": "^5.0.1"
   },
   "engine": "node >= 5.9.0",
   "nyc": {

ui/package-lock.json 143(+143 -0)

diff --git a/ui/package-lock.json b/ui/package-lock.json
index 7c0604d..30fd603 100644
--- a/ui/package-lock.json
+++ b/ui/package-lock.json
@@ -6264,6 +6264,37 @@
         "glogg": "^1.0.0"
       }
     },
+    "happypack": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/happypack/-/happypack-5.0.1.tgz",
+      "integrity": "sha512-AzXVxLzX0mtv0T40Kic72rfcGK4Y2b/cDdtcyw+e+V/13ozl7x0+EZ4hvrL1rJ8MoefR9+FfUJQsK2irH0GWOw==",
+      "dev": true,
+      "requires": {
+        "async": "1.5.0",
+        "json-stringify-safe": "5.0.1",
+        "loader-utils": "1.1.0",
+        "serialize-error": "^2.1.0"
+      },
+      "dependencies": {
+        "async": {
+          "version": "1.5.0",
+          "resolved": "https://registry.npmjs.org/async/-/async-1.5.0.tgz",
+          "integrity": "sha1-J5ZkJyNXOFlWVjP8YnRES+4vjOM=",
+          "dev": true
+        },
+        "loader-utils": {
+          "version": "1.1.0",
+          "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz",
+          "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=",
+          "dev": true,
+          "requires": {
+            "big.js": "^3.1.3",
+            "emojis-list": "^2.0.0",
+            "json5": "^0.5.0"
+          }
+        }
+      }
+    },
     "har-schema": {
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz",
@@ -12928,6 +12959,12 @@
         }
       }
     },
+    "serialize-error": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-2.1.0.tgz",
+      "integrity": "sha1-ULZ51WNc34Rme9yOWa9OW4HV9go=",
+      "dev": true
+    },
     "serialize-javascript": {
       "version": "1.5.0",
       "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.5.0.tgz",
@@ -15377,6 +15414,103 @@
       "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=",
       "dev": true
     },
+    "uglifyjs-webpack-plugin": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.3.0.tgz",
+      "integrity": "sha512-ovHIch0AMlxjD/97j9AYovZxG5wnHOPkL7T1GKochBADp/Zwc44pEWNqpKl1Loupp1WhFg7SlYmHZRUfdAacgw==",
+      "dev": true,
+      "requires": {
+        "cacache": "^10.0.4",
+        "find-cache-dir": "^1.0.0",
+        "schema-utils": "^0.4.5",
+        "serialize-javascript": "^1.4.0",
+        "source-map": "^0.6.1",
+        "uglify-es": "^3.3.4",
+        "webpack-sources": "^1.1.0",
+        "worker-farm": "^1.5.2"
+      },
+      "dependencies": {
+        "ajv": {
+          "version": "6.7.0",
+          "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.7.0.tgz",
+          "integrity": "sha512-RZXPviBTtfmtka9n9sy1N5M5b82CbxWIR6HIis4s3WQTXDJamc/0gpCWNGz6EWdWp4DOfjzJfhz/AS9zVPjjWg==",
+          "dev": true,
+          "requires": {
+            "fast-deep-equal": "^2.0.1",
+            "fast-json-stable-stringify": "^2.0.0",
+            "json-schema-traverse": "^0.4.1",
+            "uri-js": "^4.2.2"
+          }
+        },
+        "ajv-keywords": {
+          "version": "3.3.0",
+          "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.3.0.tgz",
+          "integrity": "sha512-CMzN9S62ZOO4sA/mJZIO4S++ZM7KFWzH3PPWkveLhy4OZ9i1/VatgwWMD46w/XbGCBy7Ye0gCk+Za6mmyfKK7g==",
+          "dev": true
+        },
+        "commander": {
+          "version": "2.13.0",
+          "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz",
+          "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==",
+          "dev": true
+        },
+        "find-cache-dir": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz",
+          "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=",
+          "dev": true,
+          "requires": {
+            "commondir": "^1.0.1",
+            "make-dir": "^1.0.0",
+            "pkg-dir": "^2.0.0"
+          }
+        },
+        "find-up": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
+          "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
+          "dev": true,
+          "requires": {
+            "locate-path": "^2.0.0"
+          }
+        },
+        "pkg-dir": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz",
+          "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=",
+          "dev": true,
+          "requires": {
+            "find-up": "^2.1.0"
+          }
+        },
+        "schema-utils": {
+          "version": "0.4.7",
+          "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz",
+          "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==",
+          "dev": true,
+          "requires": {
+            "ajv": "^6.1.0",
+            "ajv-keywords": "^3.1.0"
+          }
+        },
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        },
+        "uglify-es": {
+          "version": "3.3.9",
+          "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz",
+          "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==",
+          "dev": true,
+          "requires": {
+            "commander": "~2.13.0",
+            "source-map": "~0.6.1"
+          }
+        }
+      }
+    },
     "ultron": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz",
@@ -16256,6 +16390,15 @@
       "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
       "dev": true
     },
+    "worker-farm": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.6.0.tgz",
+      "integrity": "sha512-6w+3tHbM87WnSWnENBUvA2pxJPLhQUg5LKwUQHq3r+XPhIM+Gh2R5ycbwPCyuGbNg+lPgdcnQUhuC02kJCvffQ==",
+      "dev": true,
+      "requires": {
+        "errno": "~0.1.7"
+      }
+    },
     "wrap-ansi": {
       "version": "2.1.0",
       "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
diff --git a/ui/src/app/locale/locale.constant-de_DE.json b/ui/src/app/locale/locale.constant-de_DE.json
index b777b18..dd462a3 100644
--- a/ui/src/app/locale/locale.constant-de_DE.json
+++ b/ui/src/app/locale/locale.constant-de_DE.json
@@ -1575,7 +1575,8 @@
       "ru_RU": "Russisch",
       "es_ES": "Spanisch",
       "ja_JA": "Japanisch",
-      "tr_TR": "Türkisch"
+      "tr_TR": "Türkisch",
+      "fa_IR": "Persisch"
     }
   }
 }
\ No newline at end of file
diff --git a/ui/src/app/locale/locale.constant-en_US.json b/ui/src/app/locale/locale.constant-en_US.json
index 4f12ee0..eba880d 100644
--- a/ui/src/app/locale/locale.constant-en_US.json
+++ b/ui/src/app/locale/locale.constant-en_US.json
@@ -1575,7 +1575,8 @@
             "ru_RU": "Russian",
             "es_ES": "Spanish",
             "ja_JA": "Japanese",
-            "tr_TR": "Turkish"
+            "tr_TR": "Turkish",
+            "fa_IR": "Persian"
         }
     }
 }
\ No newline at end of file
diff --git a/ui/src/app/locale/locale.constant-es_ES.json b/ui/src/app/locale/locale.constant-es_ES.json
index 8190157..afb96ce 100644
--- a/ui/src/app/locale/locale.constant-es_ES.json
+++ b/ui/src/app/locale/locale.constant-es_ES.json
@@ -1575,7 +1575,8 @@
             "ru_RU": "Ruso",
             "es_ES": "Español",
             "ja_JA": "Japonés",
-            "tr_TR": "Turco"
+            "tr_TR": "Turco",
+            "fa_IR": "Persa"
         }
     }
 }
\ No newline at end of file
diff --git a/ui/src/app/locale/locale.constant-fa_IR.json b/ui/src/app/locale/locale.constant-fa_IR.json
new file mode 100644
index 0000000..de9b2a4
--- /dev/null
+++ b/ui/src/app/locale/locale.constant-fa_IR.json
@@ -0,0 +1,1582 @@
+{
+    "access": {
+        "unauthorized": "غير مجاز",
+        "unauthorized-access": "دسترسي غير مجاز",
+        "unauthorized-access-text": "!شما بايد وارد شويد تا به اين منبع دسترسي پيدا کنيد",
+        "access-forbidden": "دسترسي ممنوع",
+        "access-forbidden-text": "!اگر هنوز تمايل داريد به اينجا دسترسي پيدا کنيد، تلاش کنيد با نام کاربري ديگري وارد شويد<br/>.شما حق دسترسي به اينجا را نداريد",
+        "refresh-token-expired": "اين بخش، منقضي شده است",
+        "refresh-token-failed": "بازيابي اين بخش ممکن نيست"
+    },
+    "action": {
+        "activate": "فعال سازي",
+        "suspend": "معلّق",
+        "save": "ذخيره سازي",
+        "saveAs": "ذخيره سازي در",
+        "cancel": "لغو",
+        "ok": "قبول",
+        "delete": "حذف",
+        "add": "اضافه",
+        "yes": "بله",
+        "no": "خير",
+        "update": "به روز کردن",
+        "remove": "حذف",
+        "search": "جستجو",
+        "clear-search": "پاک کردن جستجو",
+        "assign": "تخصيص",
+        "unassign": "لغو تخصيص",
+        "share": "به اشتراک گذاري",
+        "make-private": "شخصي سازي",
+        "apply": "اعمال",
+        "apply-changes": "اعمال تغييرات",
+        "edit-mode": "حالت ويرايش",
+        "enter-edit-mode": "ورود به حالت ويرايش",
+        "decline-changes": "عدم پذيرش تغييرات",
+        "close": "بستن",
+        "back": "بازگشت",
+        "run": "اجرا",
+        "sign-in": "!ورود",
+        "edit": "ويرايش",
+        "view": "نمايش",
+        "create": "ايجاد",
+        "drag": "کشيدن",
+        "refresh": "بازيابي",
+        "undo": "برگرداندن آخرين عمل",
+        "copy": "رونوشت",
+        "paste": "الصاق رونوشت",
+        "copy-reference": "رونوشت مرجع",
+        "paste-reference": "رونوشت مرجع",
+        "import": "وارد کردن",
+        "export": "صدور",
+        "share-via": "{{provider}} اشتراک گذاري از طريق" 
+	},
+    "aggregation": {
+        "aggregation": "تجميع",
+        "function": "تابع تجميع داده ها",
+        "limit": "بيشترين مقادير",
+        "group-interval": "فاصله گروه بندي",
+        "min": "کمترين",
+        "max": "بيشترين",
+        "avg": "ميانگين",
+        "sum": "جمع",
+        "count": "شمارش",
+        "none": "هيچکدام"
+    },
+    "admin": {
+        "general": "عمومي",
+        "general-settings": "تنظيمات عمومي",
+        "outgoing-mail": "پيام خروجي",
+        "outgoing-mail-settings": "تنظيمات پيام خروجي",
+        "system-settings": "تنظيمات سيستم",
+        "test-mail-sent": "!ارسال پيام آزمايشي موفقيت آميز بود",
+        "base-url": "مبنا URL",
+        "base-url-required": ".مبنا مورد نياز است URL",
+        "mail-from": "... پيام از",
+        "mail-from-required": ".پيام از ... مورد نياز است",
+        "smtp-protocol": "SMTP قرارداد",
+        "smtp-host": "SMTP ميزبان",
+        "smtp-host-required": ".مورد نياز است SMTP ميزبان",
+        "smtp-port": "SMTP درگاه",
+        "smtp-port-required": ".فراهم کنيد SMTP شما بايد يک درگاه",
+        "smtp-port-invalid": ".معتبر باشد SMTP به نظر نمي آيد يک درگاه",
+        "timeout-msec": "مهلت (msec)",
+        "timeout-required": ".مهلت مورد نياز است",
+        "timeout-invalid": ".مهلت، به نظر نمي آيد معتبر باشد",
+        "enable-tls": "TLS فعال سازي",
+        "send-test-mail": "ارسال پيام آزمايشي"
+    },
+    "alarm": {
+        "alarm": "هشدار",
+        "alarms": "هشدارها",
+        "select-alarm": "انتخاب هشدار",
+        "no-alarms-matching": ".يافت نشد '{{entity}}'  هيچ هشداري مطابق",
+        "alarm-required": "هشدار مورد نياز است",
+        "alarm-status": "وضعيت هشدار",
+        "search-status": {
+            "ANY": "هر",
+            "ACTIVE": "فعال",
+            "CLEARED": "پاک شده",
+            "ACK": "تصديق شده",
+            "UNACK": "تصديق نشده"
+        },
+        "display-status": {
+            "ACTIVE_UNACK": "تصديق نشده فعال",
+            "ACTIVE_ACK": "تصديق شده فعال",
+            "CLEARED_UNACK": "تصديق نشده پاک شده",
+            "CLEARED_ACK": "تصديق شده پاک شده"
+        },
+        "no-alarms-prompt": "هيچ هشداري يافت نشد",
+        "created-time": "زمان ايجاد",
+        "type": "نوع",
+        "severity": "شدت",
+        "originator": "مبدأ",
+        "originator-type": "نوع مبدأ",
+        "details": "جزئيات",
+        "status": "وضعيت",
+        "alarm-details": "جزئيات هشدار",
+        "start-time": "زمان شروع",
+        "end-time": "زمان پايان",
+        "ack-time": "زمان تصديق",
+        "clear-time": "زمان پاک شدن",
+        "severity-critical": "بحراني",
+        "severity-major": "مهم",
+        "severity-minor": "جزئي",
+        "severity-warning": "اخطار",
+        "severity-indeterminate": "نامشخص",
+        "acknowledge": "تصديق",
+        "clear": "پاک کردن",
+        "search": "جستجوي هشدارها",
+        "selected-alarms": "اننخاب شده { count, plural, 1 {1 هشدار} other {# هشدارها} }",
+        "no-data": "هيچ داده اي براي نمايش نيست",
+        "polling-interval": "هشدار دهنده فاصله نمونه برداري (sec)",
+        "polling-interval-required": ".هشدار دهنده فاصله نمونه برداري مورد نياز است",
+        "min-polling-interval-message": ".حداقل فاصله مجاز نمونه برداري، 1 ثانيه است",
+        "aknowledge-alarms-title": "{ count, plural, 1 {1 هشدار} other {# هشدارها} } تصديق",
+        "aknowledge-alarms-text": "اطمينان داريد؟ { count, plural, 1 {1 هشدار} other {# هشدارها} } آيا شما از تصديق",
+        "aknowledge-alarm-title": "تصديق هشدار",
+        "aknowledge-alarm-text": "آيا شما از تصديق هشدار اطمينان داريد؟",
+        "clear-alarms-title": "{ count, plural, 1 {1 هشدار} other {# هشدارها} } پاک کردن",
+        "clear-alarms-text": "اطمينان داريد؟ { count, plural, 1 {1 هشدار} other {# هشدارها} } آيا شما از پاک کردن",
+        "clear-alarm-title": "پاک کردن هشدار",
+        "clear-alarm-text": "آيا شما از پاک کردن هشدار اطمينان داريد؟",
+        "alarm-status-filter": "فيلتر وضعيت هشدار"
+    },
+    "alias": {
+        "add": "افزودن نام مستعار",
+        "edit": "ويرايش نام مستعار",
+        "name": "نام مستعار",
+        "name-required": "نام مستعار مورد نياز است",
+        "duplicate-alias": ".در حال حاضر نام مستعار مشابهي وجود دارد",
+        "filter-type-single-entity": "موجودي تکي",
+        "filter-type-entity-list": "ليست موجودي",
+        "filter-type-entity-name": "نام موجودي",
+        "filter-type-state-entity": "موجودي از وضعيت داشبورد",
+        "filter-type-state-entity-description": "پارامترهاي موجودي گرفته شده از وضعيت داشبورد",
+        "filter-type-asset-type": "نوع دارايي",
+        "filter-type-asset-type-description": "'{{assetType}}' دارايي هاي نوع",
+        "filter-type-asset-type-and-name-description": ".شروع مي شود '{{prefix}}' که نامشان با '{{assetType}}' دارايي هاي نوع",
+        "filter-type-device-type": "نوع دستگاه",
+        "filter-type-device-type-description": "'{{deviceType}}' دستگاه هاي نوع",
+        "filter-type-device-type-and-name-description": ".شروع مي شود '{{prefix}}' که نامشان با '{{deviceType}}' دستگاه هاي نوع",
+        "filter-type-entity-view-type": "نوع نمايش موجودي",
+        "filter-type-entity-view-type-description": "'{{entityView}}' نمايش هاي موجودي نوع ",
+        "filter-type-entity-view-type-and-name-description": ".شروع مي شود '{{prefix}}' که نامشان با '{{entityView}}' نمايش هاي موجودي نوع",
+        "filter-type-relations-query": "پرس و جو درمورد ارتباطات",
+        "filter-type-relations-query-description": ".  دارند {{direction}} {{rootEntity}} را {{relationType}} که ارتباط {{entities}}",
+        "filter-type-asset-search-query": "پرس و جو درمورد جستجوي دارايي",
+        "filter-type-asset-search-query-description": ".دارند {{direction}} {{rootEntity}} را {{relationType}} که ارتباط{{assetTypes}} دارايي ها از انواع",
+        "filter-type-device-search-query": "پرس و چو درمورد جستجوي دستگاه",
+        "filter-type-device-search-query-description": ".دارند {{direction}} {{rootEntity}} را {{relationType}} که ارتباط{{deviceTypes}} دستگاه ها از انواع",
+        "filter-type-entity-view-search-query": "پرس و جو درمورد جستجوي نمايش موجودي",
+        "filter-type-entity-view-search-query-description": ".دارند {{direction}} {{rootEntity}} را {{relationType}} که ارتباط{{entityViewTypes}} نمايش هاي موجودي از انواع",
+        "entity-filter": "فيلتر موجودي",
+        "resolve-multiple": "تصميم با توجه به موجودي هاي متعدد",
+        "filter-type": "نوع فيلتر",
+        "filter-type-required": ".نوع فيلتر مورد نياز است",
+        "entity-filter-no-entity-matched": ".هيچ موجودي منطبق بر فيلتر مشخص شده يافت نشد",
+        "no-entity-filter-specified": ".هيچ فيلتر موجودي اي تعيين نشده است",
+        "root-state-entity": "موجودي وضعيت داشبورد به عنوان پايه استفاده شود",
+        "root-entity": "موجودي پايه",
+        "state-entity-parameter-name": "نام پارامتر موجودي وضعيت",
+        "default-state-entity": "موجودي وضعيت پيش فرض",
+        "default-entity-parameter-name": "به صورت پيش فرض",
+        "max-relation-level": "بالاترين سطح ارتباط",
+        "unlimited-level": "سطح نامحدود",
+        "state-entity": "موجودي وضعيت داشبورد",
+        "all-entities": "تمام موجودي ها",
+        "any-relation": "هر"
+    },
+    "asset": {
+        "asset": "دارايي",
+        "assets": "دارايي ها",
+        "management": "مديريت دارايي",
+        "view-assets": "نمايش دارايي ها",
+        "add": "افزودن دارايي",
+        "assign-to-customer": "تخصيص به مشتري",
+        "assign-asset-to-customer": "تخصيص دارايي(ها) به مشتري",
+        "assign-asset-to-customer-text": "لطفا دارايي ها را انتخاب کنيد تا به مشتري تخصيص يابد",
+        "no-assets-text": "هيچ دارايي اي يافت نشد",
+        "assign-to-customer-text": "لطفا مشتري را انتخاب کنيد تا دارايي(ها) تخصيص يابد",
+        "public": "عمومي",
+        "assignedToCustomer": "تخصيص يافته به مشتري",
+        "make-public": "عمومي سازي دارايي",
+        "make-private": "شخصي سازي دارايي",
+        "unassign-from-customer": "لغو تخصيص از مشتري",
+        "delete": "حذف دارايي",
+        "asset-public": "دارايي عمومي است",
+        "asset-type": "نوع دارايي",
+        "asset-type-required": ".نوع دارايي مورد نياز است",
+        "select-asset-type": "انتخاب کردن نوع دارايي",
+        "enter-asset-type": "وارد کردن نوع دارايي",
+        "any-asset": "هر دارايي",
+        "no-asset-types-matching": ".يافت نشد '{{entitySubtype}}' هيچ دارايي منطبق بر",
+        "asset-type-list-empty": ".هيچيک از انواع دارايي انتخاب نشد",
+        "asset-types": "انواع دارايي",
+        "name": "نام",
+        "name-required": ".نام مورد نياز است",
+        "description": "توصيف",
+        "type": "نوع",
+        "type-required": ".نوع مورد نياز است",
+        "details": "جزئيات",
+        "events": "رويدادها",
+        "add-asset-text": "افزودن دارايي جديد",
+        "asset-details": "جزئيات دارايي",
+        "assign-assets": "تخصيص دارايي ها",
+        "assign-assets-text": "به مشتري { count, plural, 1 {1 دارايي} other {# دارايي} } تخصيص",
+        "delete-assets": "حذف دارايي ها",
+        "unassign-assets": "لغو تخصيص دارايي ها",
+        "unassign-assets-action-title": "از مشتري { count, plural, 1 {1 دارايي} other {# دارايي} } لغو تخصيص",
+        "assign-new-asset": "تخصيص دارايي جديد",
+        "delete-asset-title": "مطمئنيد؟ '{{assetName}}' آيا از حذف دارايي",
+        "delete-asset-text": ".مراقب باشيد، پس از تأييد، دارايي و تمام داده هاي مربوطه غير قابل بازيابي مي شوند",
+        "delete-assets-title": "مطمئنيد؟ { count, plural, 1 {1 دارايي} other {# دارايي} } آيا از حذف",
+        "delete-assets-action-title": "{ count, plural, 1 {1 دارايي} other {# دارايي} } حذف",
+        "delete-assets-text": ".مراقب باشيد، پس از تأييد، تمام دارايي هاي انتخاب شده حذف، و تمامي داده هاي مربوطه غير قابل بازيابي مي شوند",
+        "make-public-asset-title": "مطمئنيد؟ '{{assetName}}' آيا از عمومي سازي",
+        "make-public-asset-text": ".پس از تأييد، دارايي و تمامي داده هايش عمومي و قابل دسترسي براي ديگران مي شود",
+        "make-private-asset-title": "مطمئنيد؟ '{{assetName}}' آيا از شخصي سازي دارايي",
+        "make-private-asset-text": ".پس از تأييد، دارايي و تمامي داده هايش شخصي و خارج از دسترس ديگران مي شوند",
+        "unassign-asset-title": "مطمئنيد؟ '{{assetName}}' آيا از لغو تخصيص دارايي",
+        "unassign-asset-text": ".پس از تأييد، دارايي، لغو تخصيص و خارج از دسترس مشتري مي شود",
+        "unassign-asset": "لغو تخصيص دارايي",
+        "unassign-assets-title": "مطمئنيد؟ { count, plural, 1 {1 دارايي} other {# دارايي} } آيا از لغو تخصيص",
+        "unassign-assets-text": ".پس از تأييد، تمام دارايي هاي انتخاب شده، لغو تخصيص و خارج از دسترس مشتري مي شوند",
+        "copyId": "دارايي ID رونوشت از",
+        "idCopiedMessage": "دارايي در حافظه موقت رونوشت شد ID",
+        "select-asset": "انتخاب دارايي",
+        "no-assets-matching": ".يافت نشد '{{entity}}' هيچ دارايي منطبق بر",
+        "asset-required": "دارايي مود نياز است",
+        "name-starts-with": "نام دارايي شروع مي شود با"
+    },
+    "attribute": {
+        "attributes": "ويژگي ها",
+        "latest-telemetry": "آخرين سنجش",
+        "attributes-scope": "حوزه ويژگي هاي موجودي",
+        "scope-latest-telemetry": "آخرين سنجش",
+        "scope-client": "ويژگي هاي مشتري",
+        "scope-server": "ويژگي هاي سِروِر",
+        "scope-shared": "ويژگي هاي مشترک",
+        "add": "افزودن ويژگي ها",
+        "key": "کليد",
+        "last-update-time": "آخرين زمان به روز رساني",
+        "key-required": ".کليد ويژگي مورد نياز است",
+        "value": "مقدار",
+        "value-required": ".مقدار ويژگي مورد نياز است",
+        "delete-attributes-title": "مطمئنيد؟ { count, plural, 1 {1 ويژگي} other {# ويژگي} } آيا از حذف",
+        "delete-attributes-text": ".مراقب باشيد، پس از تأييد، تمام ويژگي هاي انتخاب شده حذف مي گردند",
+        "delete-attributes": "حذف ويژگي ها",
+        "enter-attribute-value": "وارد کردن مقدار ويژگي",
+        "show-on-widget": "نمايش بر ويجت",
+        "widget-mode": "حالت ويجت",
+        "next-widget": "ويجت بعد",
+        "prev-widget": "ويجت قبل",
+        "add-to-dashboard": "افزودن به داشبورد",
+        "add-widget-to-dashboard": "افزودن ويجت به داشبورد",
+        "selected-attributes": "انتخاب شدند { count, plural, 1 {1 ويژگي} other {# ويژگي} }",
+        "selected-telemetry": "انتخاب شد { count, plural, 1 {1 واحد سنجش} other {# واحد سنجش} }"
+    },
+    "audit-log": {
+        "audit": "بازبيني",
+        "audit-logs": "داده هاي ثبت شده از بازبيني",
+        "timestamp": "برچسب زمان",
+        "entity-type": "نوع موحودي",
+        "entity-name": "نام موجودي",
+        "user": "کاربر",
+        "type": "نوع",
+        "status": "وضعيت",
+        "details": "جزئيات",
+        "type-added": "اضافه شده",
+        "type-deleted": "حذف شده",
+        "type-updated": "به روز",
+        "type-attributes-updated": "ويژگي ها به روز شد",
+        "type-attributes-deleted": "ويژگي ها حذف شد",
+        "type-rpc-call": "RPC فراخواني",
+        "type-credentials-updated": "اعتبارنامه ها به روز شد",
+        "type-assigned-to-customer": "به مشتري تخصيص يافت",
+        "type-unassigned-from-customer": "از مشتري لغو تخصيص شد",
+        "type-activated": "فعال شد",
+        "type-suspended": "معلق",
+        "type-credentials-read": "اعتبارنامه ها خوانده شد",
+        "type-attributes-read": "ويژگي ها خوانده شد",
+        "type-relation-add-or-update": "ارتباط به روز شد",
+        "type-relation-delete": "ارتباط حذف شد",
+        "type-relations-delete": "تمام ارتباطات حذف شد",
+        "type-alarm-ack": "تصديق شده",
+        "type-alarm-clear": "پاک شده",
+        "status-success": "موفقيت",
+        "status-failure": "عدم موفقيت",
+        "audit-log-details": "بازبيني جزئيات ثبت داده ها",
+        "no-audit-logs-prompt": "هيچ داده ثبت شده اي يافت نشد",
+        "action-data": "داده هاي اقدام",
+        "failure-details": "جزئيات عدم موفقيت",
+        "search": "جستجوي داده هاي ثبت شده از بازبيني",
+        "clear-search": "پاک کردن جستجو"
+    },
+    "confirm-on-exit": {
+        "message": "شما تغييراتي ذخيره نشده داريد. از ترک اين صفحه مطمئنيد؟",
+        "html-message": "از ترک اين صفحه مطمئنيد؟<br/>.شما تغييراتي ذخيره نشده داريد",
+        "title": "تغييرات ذخيره نشده   "
+		},
+    "contact": {
+        "country": "کشور",
+        "city": "شهر",
+        "state": "استان / ايالت",
+        "postal-code": "کد پستي",
+        "postal-code-invalid": ".قالب کد پستي نامعتبر است",
+        "address": "نشاني",
+        "address2": "2 نشاني",
+        "phone": "تلفن",
+        "email": "پست الکترونيک",
+        "no-address": "بدون آدرس"
+    },
+    "common": {
+        "username": "نام کاربري",
+        "password": "رمز عبور",
+        "enter-username": "وارد کردن نام کاربري",
+        "enter-password": "وارد کردن رمز عبور",
+        "enter-search": "وارد کردن جستجو"
+    },
+    "content-type": {
+        "json": "JSON",
+        "text": "Text",
+        "binary": "Binary (Base64)"
+    },
+    "customer": {
+        "customer": "مشتري",
+        "customers": "مشتريان",
+        "management": "مديريت مشتري",
+        "dashboard": "داشبورد مشتري",
+        "dashboards": "داشبوردهاي مشتري",
+        "devices": "دستگاه هاي مشتري",
+        "entity-views": "نمايش موجودي مشتري",
+        "assets": "دارايي هاي مشتري",
+        "public-dashboards": "داشبوردهاي عمومي",
+        "public-devices": "دستگاه هاي عمومي",
+        "public-assets": "دارايي هاي عمومي",
+        "public-entity-views": "نمايش موجودي عمومي",
+        "add": "افزودن مشتري",
+        "delete": "حذف مشتري",
+        "manage-customer-users": "مديريت کاربرهاي مشتري",
+        "manage-customer-devices": "مديريت دستگاه هاي مشتري",
+        "manage-customer-dashboards": "مديريت داشبوردهاي مشتري",
+        "manage-public-devices": "مديريت دستگاه هاي عمومي",
+        "manage-public-dashboards": "مديريت داشبوردهاي عمومي",
+        "manage-customer-assets": "مديريت دارايي هاي مشتري",
+        "manage-public-assets": "مديريت دارايي هاي عمومي",
+        "add-customer-text": "افزودن مشتري جديد",
+        "no-customers-text": "هيچ مشتري اي يافت نشد",
+        "customer-details": "جزئيات اطلاعات مشتري",
+        "delete-customer-title": "مطمئنيد؟ '{{customerTitle}}' از حذف مشتري",
+        "delete-customer-text": ".مراقب باشيد، پس از تأييد، مشتري و تمامي داده هاي مربوطه، غير قابل بازيابي مي شوند",
+        "delete-customers-title": "مطمئنيد؟ { count, plural, 1 {1 مشتري} other {# مشتري} } از حذف",
+        "delete-customers-action-title": "{ count, plural, 1 {1 مشتري} other {# مشتري} } حذف",
+        "delete-customers-text": ".مراقب باشيد، پس از تأييد، تمام مشتريانِ انتخاب شده حذف، و تمامي داده هاي مربوطه غير قابل دسترسي مي شوند",
+        "manage-users": "مديريت کاربرها",
+        "manage-assets": "مديريت دارايي ها",
+        "manage-devices": "مديريت دستگاه ها",
+        "manage-dashboards": "مديريت داشبوردها",
+        "title": "عنوان",
+        "title-required": ".عنوان مورد نياز است",
+        "description": "توصيف",
+        "details": "جزئيات",
+        "events": "رويدادها",
+        "copyId": "مشتري ID رونوشت از",
+        "idCopiedMessage": "مشتري در حافظه موقت رونوشت شد ID",
+        "select-customer": "انتخاب مشتري",
+        "no-customers-matching": ".يافت نشد '{{entity}}' هيچ مشتري منطبق بر",
+        "customer-required": "مشتري مورد نياز است",
+        "select-default-customer": "انتخاب مشتري پيش فرض",
+        "default-customer": "مشتري پيش فرض",
+        "default-customer-required": "جهت عيب يابي داشبورد در سطح کاربر مياني، مشتري پيش فرض مورد نياز است"
+    },
+    "datetime": {
+        "date-from": "تاريخ از",
+        "time-from": "زمان از",
+        "date-to": "تاريخ تا",
+        "time-to": "زمان تا"
+    },
+    "dashboard": {
+        "dashboard": "داشبورد",
+        "dashboards": "داشبوردها",
+        "management": "مديريت داشبورد",
+        "view-dashboards": "نمايش داشبوردها",
+        "add": "افزودن داشبورد",
+        "assign-dashboard-to-customer": "تخصيص داشبورد(ها) به مشتري",
+        "assign-dashboard-to-customer-text": "لطفا داشبوردها را، جهت تخصيص به مشتري، انتخاب کنيد",
+        "assign-to-customer-text": "لطفا مشتري را، جهت تخصيص داشبورد(ها)، انتخاب کنيد",
+        "assign-to-customer": "تخصيص به مشتري",
+        "unassign-from-customer": "لغو تخصيص از مشتري",
+        "make-public": "عمومي سازي مشتري",
+        "make-private": "شخصي سازي داشبورد",
+        "manage-assigned-customers": "مديريت مشتريان تخصيص داده شده",
+        "assigned-customers": "مشتريان تخصيص داده شده",
+        "assign-to-customers": "تخصيص داشبورد(ها) به مشتريان",
+        "assign-to-customers-text": "لطفا مشتريان را، جهت تخصيص داشبورد(ها)، انتخاب کنيد",
+        "unassign-from-customers": "لغو تخصيص داشبوردها از مشتريان",
+        "unassign-from-customers-text": "لطفا مشتريان را، جهت لغو تخصيص از داشبورد(ها)، انتخاب کنيد",
+        "no-dashboards-text": "هيچ داشبوردي يافت نشد",
+        "no-widgets": "هيچ ويجتي پيکربندي نشده است",
+        "add-widget": "افزودن ويجت جديد",
+        "title": "عنوان",
+        "select-widget-title": "انتخاب ويجت",
+        "select-widget-subtitle": "ليست انواع ويجت هاي در دسترس",
+        "delete": "حذف داشبورد",
+        "title-required": ".عنوان مورد نياز است",
+        "description": "توصيف",
+        "details": "جزئيات",
+        "dashboard-details": "جزئيات داشبورد",
+        "add-dashboard-text": "افزودن داشبورد جديد",
+        "assign-dashboards": "تخصيص داشبوردها",
+        "assign-new-dashboard": "تخصيص داشبورد جديد",
+        "assign-dashboards-text": "به مشتريان { count, plural, 1 {1 داشبورد} other {# داشبورد} } تخصيص",
+        "unassign-dashboards-action-text": "از مشتريان { count, plural, 1 {1 داشبورد} other {# داشبورد} } لغو تخصيص",
+        "delete-dashboards": "حذف داشبوردها",
+        "unassign-dashboards": "لغو تخصيص داشبوردها",
+        "unassign-dashboards-action-title": "از مشتري { count, plural, 1 {1 داشبورد} other {# داشبورد} } لغو تخصيص",
+        "delete-dashboard-title": "مطمئنيد؟ '{{dashboardTitle}}' از حذف",
+        "delete-dashboard-text": ".مراقب باشيد، پس از تأييد، داشبورد و تمامي داده هاي مربوطه، غير قابل بازيابي مي شوند",
+        "delete-dashboards-title": "مطمئنيد؟ { count, plural, 1 {1 داشبورد} other {# داشبورد} } از حذف",
+        "delete-dashboards-action-title": "{ count, plural, 1 {1 داشبورد} other {# داشبورد} } حذف",
+        "delete-dashboards-text": ".مراقب باشيد، پس از تأييد، تمام داشبوردهاي انتخاب شده حذف، و تمامي داده هاي مربوطه غير قابل بازيابي مي شوند ",
+        "unassign-dashboard-title": "مطمئنيد؟ '{{dashboardTitle}}' از لغو تخصيص داشبورد",
+        "unassign-dashboard-text": ".پس از تأييد، داشبورد، لغو تخصيص و خارج از دسترس مشتري مي شود",
+        "unassign-dashboard": "لغو تخصيص داشبورد",
+        "unassign-dashboards-title": "مطمئنيد؟ { count, plural, 1 {1 داشبورد} other {# داشبورد} } از لغو تخصيص",
+        "unassign-dashboards-text": ".پس از تأييد، تمام داشبوردهاي انتخاب شده، لغو تخصيص و خارج از دسترس مشتري مي شوند",
+        "public-dashboard-title": "داشبورد اکنون عمومي است",
+        "public-dashboard-text": ":</a>قابل دسترسي است<a href='{{publicLink}}' target='_blank'> اکنون عمومي بوده و از طريق پيوند عمومي ديگر ، <b>{{dashboardTitle}}</b> ،داشبورد شما",
+        "public-dashboard-notice": ".فراموش نکنيد براي دسترسي به داده هاي دستگاه هاي مربوطه، آنها را عمومي نماييد </b>:توجه<b>",
+        "make-private-dashboard-title": "مطمئنيد؟ '{{dashboardTitle}}' از شخصي سازي داشبورد",
+        "make-private-dashboard-text": ".پس از تأييد، داشبورد، شخصي و خارج از دسترس ديگران مي شود",
+        "make-private-dashboard": "شخصي سازي داشبورد",
+        "socialshare-text": "ThingsBoard طراحي شده توسط '{{dashboardTitle}}'",
+        "socialshare-title": "ThingsBoard طراحي شده توسط '{{dashboardTitle}}'",
+        "select-dashboard": "انتخاب داشبورد",
+        "no-dashboards-matching": ".يافت نشد '{{entity}}' هيچ داشبوردي منطبق بر",
+        "dashboard-required": ".داشبورد مورد نياز است",
+        "select-existing": "انتخاب داشبورد موجود",
+        "create-new": "ايجاد داشبورد جديد",
+        "new-dashboard-title": "عنوان داشبورد جديد",
+        "open-dashboard": "باز کردن داشبورد",
+        "set-background": "تنظيم پس زمينه",
+        "background-color": "رنگ پس زمينه",
+        "background-image": "تصوير پس زمينه",
+        "background-size-mode": "حالت اندازه پس زمينه",
+        "no-image": "هيچ تصويري انتخاب نشد",
+        "drop-image": ".جهت بارگذاري يک تصوير، آن را با موس کِشيده و رها کنيد، و يا روي آن کليک نماييد",
+        "settings": "تنظيمات",
+        "columns-count": "شمارش ستون ها",
+        "columns-count-required": ".شمارش ستون ها مورد نياز است",
+        "min-columns-count-message": ".کمترين تعداد مجاز ستون ها 10 عدد است",
+        "max-columns-count-message": ".بيشترين تعداد مجاز ستون ها 1000 عدد است",
+        "widgets-margins": "حاشيه بين ويجت ها",
+        "horizontal-margin": "حاشيه افقي",
+        "horizontal-margin-required": ".مقدار حاشيه افقي مورد نياز است",
+        "min-horizontal-margin-message": ".کمترين مقدار مجاز حاشيه افقي 0 است",
+        "max-horizontal-margin-message": ".بيشترين مقدار مجاز حاشيه افقي 50 است",
+        "vertical-margin": "حاشيه عمودي",
+        "vertical-margin-required": ".حاشيه عمودي مورد نياز است",
+        "min-vertical-margin-message": ".کمترين مقدار مجاز حاشيه عمودي 0 است",
+        "max-vertical-margin-message": ".بيشترين مقدار مجاز حاشيه افقي 50 است",
+        "autofill-height": "تنظيم خودکار ارتفاع چيدمان طرح",
+        "mobile-layout": "تنظيمات چيدمان طرح در تلفن همراه",
+        "mobile-row-height": "(px) ارتفاع رديف در تلفن همراه",
+        "mobile-row-height-required": ".مقدار ارتفاع ردبف در تلفن همراه مورد نياز است",
+        "min-mobile-row-height-message": ".کمترين مقدار مجاز ارتفاع رديف در تلفن همراه 5 پيکسل است",
+        "max-mobile-row-height-message": ".بيشترين مقدار مجاز ارتفاع رديف در تلفن همراه 200 پيکسل است",
+        "display-title": "نمايش عنوان داشبورد",
+        "toolbar-always-open": "باز نگه داشتن نوار ابزار",
+        "title-color": "رنگ عنوان",
+        "display-dashboards-selection": "نمايش انتخاب داشبوردها",
+        "display-entities-selection": "نمايش انتخاب موجودي ها",
+        "display-dashboard-timewindow": "نمايش پنجره زمان",
+        "display-dashboard-export": "نمايش صدور",
+        "import": "وارد کردن داشبورد",
+        "export": "صادر کردن داشبورد",
+        "export-failed-error": "{{error}} :صدور داشبورد ممکن نيست",
+        "create-new-dashboard": "ايجاد داشبورد جديد",
+        "dashboard-file": "پرونده داشبورد",
+        "invalid-dashboard-file-error": ".وارد کردن داشبورد ممکن نيست: ساختار داده داشبورد نامعتبر است",
+        "dashboard-import-missing-aliases-title": "پيکربندي نامهاي مستعار استفاده شده توسط داشبوردِ وارده",
+        "create-new-widget": "ايجاد ويجت جديد",
+        "import-widget": "وارد کردن ويجت",
+        "widget-file": "پرونده ويجت",
+        "invalid-widget-file-error": ".وارد کردن ويجت ممکن نيست: ساختار داده ويجت نامعتبر است",
+        "widget-import-missing-aliases-title": "پيکربندي نامهاي مستعار استفاده شده توسط ويجتِ وارده",
+        "open-toolbar": "باز کردن نوار ابزار داشبورد",
+        "close-toolbar": "بستن نوار ابزار",
+        "configuration-error": "خطاي پيکربندي",
+        "alias-resolution-error-title": "خطاي پيکربندي نامهاي مستعار داشبورد",
+        "invalid-aliases-config": ".لطفا جهت حل اين موضوع با مسئول مربوط به خود تماس بگيريد<br/>.يافتن دستگاهي منطبق بر فبلتر بعضي نامهاي مستعار ممکن نيست",
+        "select-devices": "انتخاب دستگاه ها",
+        "assignedToCustomer": "تخصيص يافته به مشتري",
+        "assignedToCustomers": "تخصيص يافته به مشتريان",
+        "public": "عمومي",
+        "public-link": "پيوند عمومي",
+        "copy-public-link": "رونوشت از پيوند عمومي",
+        "public-link-copied-message": "پيوند عمومي داشبورد در حافظه موقت رونوشت شد",
+        "manage-states": "مديريت وضعيت هاي داشبورد",
+        "states": "وضعيت هاي داشبورد",
+        "search-states": "جستجوي وضعيت هاي داشبورد",
+        "selected-states": "انتخاب شدند { count, plural, 1 {1 وضعيت داشبورد} other {# وضعيت داشبورد} }",
+        "edit-state": "ويرايش وضعيت داشبورد",
+        "delete-state": "حذف وضعيت داشبورد",
+        "add-state": "افزودن وضعيت داشبورد",
+        "state": "وضعيت داشبورد",
+        "state-name": "نام",
+        "state-name-required": ".نام وضعيت داشبورد مورد نياز است",
+        "state-id": "وضعيت ID",
+        "state-id-required": ".وضعيت داشبورد مورد نياز است ID",
+        "state-id-exists": ".مشابه موجود است ID در حال حاضر وضعيت داشبوردي با",
+        "is-root-state": "وضعيت پايه",
+        "delete-state-title": "حذف وضعيت داشبورد",
+        "delete-state-text": "مطمئنيد؟ '{{stateName}}' از حذف وضعيت داشبورد با نام",
+        "show-details": "نمايش جزئيات",
+        "hide-details": "پنهان کردن جزئيات",
+        "select-state": "انتخاب وضعيت هدف",
+        "state-controller": "کنترل کننده وضعيت"
+    },
+    "datakey": {
+        "settings": "تنظيمات",
+        "advanced": "پيشرفته",
+        "label": "برچسب",
+        "color": "رنگ",
+        "units": "کارکتر خاص براي نمايش بعد از مقدار تعين شده",
+        "decimals": "تعداد ارقام بعد از مميّز شناور",
+        "data-generation-func": "تابع توليد داده",
+        "use-data-post-processing-func": "استفاده از تابع پس پردازش داده",
+        "configuration": "پيکربندي کليد داده",
+        "timeseries": "سري هاي زماني",
+        "attributes": "ويژگي ها",
+        "alarm": "حوزه هاي هشدار",
+        "timeseries-required": ".سري هاي زماني موجودي مورد نياز است",
+        "timeseries-or-attributes-required": ".سري هاي زماني / ويژگي هاي موجودي مورد نياز است",
+        "maximum-timeseries-or-attributes": "{ count, plural, 1 {.1 سري زماني / ويژگي مجاز است} other {# سري زماني / ويژگي مجازند} } بيشترين",
+        "alarm-fields-required": ".حوزه هاي هشدار مورد نياز است",
+        "function-types": "نوع توابع",
+        "function-types-required": ".نوع تابع مورد نياز است",
+        "maximum-function-types": "{ count, plural, 1 {.1 نوع تابع مجاز است} other {# نوع تابع مجازند} } بيشترين",
+        "time-description": "برچسب زماني مقدار فعلي؛",
+        "value-description": "مقدار فعلي؛",
+        "prev-value-description": "نتيجه ي فراخوانيِ تابع قبلي؛",
+        "time-prev-description": "برچسب زماني مقدار قبلي؛",
+        "prev-orig-value-description": "ممقدار اصلي قبلي"
+    },
+    "datasource": {
+        "type": "نوع منبع داده",
+        "name": "نام",
+        "add-datasource-prompt": "لطفا منبع داده را اضافه کنيد"
+    },
+    "details": {
+        "edit-mode": "حالت ويرايش",
+        "toggle-edit-mode": "حالت ويرايش را تغيير دهيد"
+    },
+    "device": {
+        "device": "دستگاه",
+        "device-required": ".دستگاه مورد نياز است",
+        "devices": "دستگاه ها",
+        "management": "مديريت دستگاه",
+        "view-devices": "نمايش دستگاه ها",
+        "device-alias": "نام مستعار دستگاه",
+        "aliases": "نامهاي مستعار دستگاه",
+        "no-alias-matching": ".يافت نشد'{{alias}}'",
+        "no-aliases-found": ".هيچ نام مستعاري يافت نشد",
+        "no-key-matching": ".يافت نشد'{{key}}'",
+        "no-keys-found": ".هيچ کليدي يافت نشد",
+        "create-new-alias": "!ايجاد يک نام مستعار جديد",
+		"create-new-key": "!ايجاد يک کليد جديد",
+        "duplicate-alias-error": ".نام مستعار در داشبورد بايد يکتا باشد<br>'{{alias}}'نام مستعار مشابه يافت شد",
+        "configure-alias": "نام مستعار '{{alias}}' پيکربندي",
+        "no-devices-matching": "مطابقت داشته باشد وجود ندارد '{{entity}}' هيچ دستگاهي که با ",
+        "alias": "نام مستعار",
+        "alias-required": ".نام مستعار مورد نياز است",
+        "remove-alias": "حذف نام مستعار دستگاه",
+        "add-alias": "افزودن نام مستعار دستگاه",
+        "name-starts-with": "اسم دستگاه شروع مي شود با",
+        "device-list": "ليست دستگاه ها",
+        "use-device-name-filter": "از فيلتر استفاده کنيد",
+        "device-list-empty": ".هيچ دستگاهي انتخاب نشده است",
+        "device-name-filter-required": ".فيلتر نام دستگاه مورد نياز است",
+        "device-name-filter-no-device-matched": ".شروع شود يافت نشد '{{device}}' هيچ دستگاهي که با",
+        "add": "افزودن دستگاه",
+        "assign-to-customer": "تخصيص به مشتري",
+		"assign-device-to-customer": "تخصيص دستگاه (ها) به مشتري",
+        "assign-device-to-customer-text": "لطفا دستگاه ها را انتخاب کنيد تا به مشتري تخصيص يابد",
+        "make-public": "عمومي سازي دستگاه",
+        "make-private": "شخصي سازي دستگاه",
+        "no-devices-text": "هيچ دستگاهي يافت نشد",
+        "assign-to-customer-text": "لطفا مشتري را انتخاب کنيد تا دستگاه(ها) تخصيص يابد",
+        "device-details": "جزئيات دستگاه",
+        "add-device-text": "افزودن دستگاه جديد",
+        "credentials": "اعتبارنامه ها",
+        "manage-credentials": "مديريت اعتبارنامه ها",
+        "delete": "حذف دستگاه",
+        "assign-devices": "تخصيص دستگاه ها",
+        "assign-devices-text": "به مشتري { count, plural, 1 {1 دستگاه} other {# دستگاه} } تخصيص",
+        "delete-devices": "حذف دستگاه ها",
+        "unassign-from-customer": "لغو تخصيص از مشتري",
+        "unassign-devices": "لغو تخصيص دستگاه ها",
+        "unassign-devices-action-title": "از مشتري { count, plural, 1 {1 دستگاه} other {# دستگاه} } لغو تخصيص",
+        "assign-new-device": "تخصيص دستگاه جديد",
+        "make-public-device-title": "مطمئنيد؟ '{{deviceName}}' از عمومي سازي دستگاه",
+        "make-public-device-text": ".پس از تأييد، دستگاه و تمامي داده هايش عمومي و قابل دسترسي براي ديگران مي شود",
+        "make-private-device-title": "مطمئنيد؟ '{{deviceName}}' از شخصي سازي دستگاه",
+        "make-private-device-text": ".پس از تأييد، دستگاه و تمامي داده هايش شخصي و خارج از دسترس ديگران مي شوند",
+        "view-credentials": "نمايش اعتبارنامه ها",
+        "delete-device-title": "مطمئنيد؟ '{{deviceName}}' از حذف",
+        "delete-device-text": ".مراقب باشيد، پس از تأييد، دستگاه و تمامي داده هاي مربوطه غير قابل بازيابي مي شوند",
+        "delete-devices-title": "مطمئنيد؟ { count, plural, 1 {1 دستگاه} other {# دستگاه} } از حذف",
+        "delete-devices-action-title": "{ count, plural, 1 {1 دستگاه} other {# دستگاه} } حذف",
+        "delete-devices-text": ".مراقب باشيد، پس از تأييد، تمام دستگاه هاي انتخاب شده، حذف، و تمامي داده هاي مربوطه غير قابل بازيابي مي شوند",
+        "unassign-device-title": "مطمئنيد؟ '{{deviceName}}' از لغو تخصيص",
+        "unassign-device-text": ".پس از تأييد، دستگاه، لغو تخصيص و خارج از دسترس مشتري مي شود",
+        "unassign-device": "لغو تخصيص دستگاه",
+        "unassign-devices-title": "مطمئنيد؟ { count, plural, 1 {1 دستگاه} other {# دستگاه} } از لغو تخصيص",
+        "unassign-devices-text": ".پس از تأييد، تمام دستگاه هاي انتخاب شده، لغو تخصيص و خارج از دسترس مشتري مي شوند",
+        "device-credentials": "اعتبارنامه هاي دستگاه",
+        "credentials-type": "نوع اعتبارنامه ها",
+        "access-token": "شناسه دسترسي",
+        "access-token-required": ".شناسه دسترسي مورد نياز است",
+        "access-token-invalid": ".طول شناسه دسترسي بايد از 1 تا 20 حرف باشد",
+        "rsa-key": "RSA کليد عمومي",
+        "rsa-key-required": ".مورد نياز است RSA کليد عمومي",
+        "secret": "محرمانه",
+        "secret-required": ".شناسه محرمانه مورد نياز است",
+        "device-type": "نوع دستگاه",
+        "device-type-required": ".نوع دستگاه مورد نياز است",
+        "select-device-type": "انتخاب نوع دستگاه",
+        "enter-device-type": "وارد کردن نوع دستگاه",
+        "any-device": "هر دستگاهي",
+        "no-device-types-matching": ".يافت نشد '{{entitySubtype}}' هيچ نوع دستگاهي منطبق بر",
+        "device-type-list-empty": ".هيچ نوع دستگاهي انتخاب نشد",
+        "device-types": "انواع دستگاه",
+        "name": "نام",
+        "name-required": ".نام مورد نياز است",
+        "description": "توصيف",
+        "events": "رويدادها",
+        "details": "جزئيات",
+        "copyId": "دستگاه ID رونوشت از",
+        "copyAccessToken": "رونوشت از شناسه دسترسي",
+        "idCopiedMessage": ".دستگاه در حافظه موقت رونوشت شد ID",
+        "accessTokenCopiedMessage": ".شناسه دسترسي دستگاه در حافظه موقت رونوشت شد",
+        "assignedToCustomer": "تخصيص يافته به مشتري",
+        "unable-delete-device-alias-title": "حذف نام مستعار دستگاه ممکن نيست",
+        "unable-delete-device-alias-text": "<br/>{{widgetsList}} :را تا زمان استفاده توسط ويجت(هاي) زير نمي توان حذف کرد ، '{{deviceAlias}}' ،نام مستعار دستگاه",
+        "is-gateway": "درگاه است",
+        "public": "عمومي",
+        "device-public": "دستگاه عمومي است",
+        "select-device": "انتخاب دستگاه"
+    },
+    "dialog": {
+        "close": "بستن گفتگو"
+    },
+    "error": {
+        "unable-to-connect": ".اتصال به سِروِر ممکن نيست! لطفا اتصال اينترنت خود را بررسي کنيد",
+        "unhandled-error-code": "{{errorCode}} :کد خطاي رسيدگي نشده",
+        "unknown-error": "خطاي ناشناخته"
+    },
+    "entity": {
+        "entity": "موجودي",
+        "entities": "موجودي ها",
+        "aliases": "نامهاي مستعار موجودي",
+        "entity-alias": "نام مستعار موجودي",
+        "unable-delete-entity-alias-title": "حذف نام مستعار موجودي ممکن نيست",
+        "unable-delete-entity-alias-text": "<br/>{{widgetsList}} :را تا زمان استفاده توسط ويجت(هاي) زير نمي توان حذف کرد ، '{{entityAlias}}' ،نام مستعار موجودي",
+        "duplicate-alias-error": ".نامهاي مستعار موجودي بايد در داشبورد، منحصر بفرد باشند<br>.يافت شد '{{alias}}' نام مستعار تکراري",
+        "missing-entity-filter-error": ".مفقود است '{{alias}}' فيلتر براي نام مستعار",
+        "configure-alias": "'{{alias}}' پيکربندي نام مستعار",
+        "alias": "نام مستعار",
+        "alias-required": ".نام مستعار موجودي مورد نياز است",
+        "remove-alias": "حذف نام مستعار موجودي",
+        "add-alias": "افزودن نام مستعار موجودي",
+        "entity-list": "ليست موجودي",
+        "entity-type": "نوع موجودي",
+        "entity-types": "انواع موجودي",
+        "entity-type-list": "ليست نوع موجودي",
+        "any-entity": "هر موجودي",
+        "enter-entity-type": "وارد کردن نوع موجودي",
+        "no-entities-matching": ".يافت نشد '{{entity}}' هيچ موجودي منطبق بر",
+        "no-entity-types-matching": ".يافت نشد '{{entityType}}' هيچ نوع موجودي منطبق بر",
+        "name-starts-with": "نام شروع مي شود با",
+        "use-entity-name-filter": "استفاده از فيلتر",
+        "entity-list-empty": ".هيچ موجودي اي انتخاب نشده است",
+        "entity-type-list-empty": ".هيچ نوع موجودي انتخاب نشده است",
+        "entity-name-filter-required": ".فيلتر نام موجودي مورد نياز است",
+        "entity-name-filter-no-entity-matched": ".شروع شود يافت نشد '{{entity}}' هيچ موجودي که با",
+        "all-subtypes": "همه",
+        "select-entities": "انتخاب موجودي ها",
+        "no-aliases-found": ".هيچ نام مستعاري يافت نشد",
+        "no-alias-matching": ".يافت نشد '{{alias}}'",
+        "create-new-alias": "!ايجاد يک نام مستعار جديد",
+        "key": "کليد",
+        "key-name": "نام کليد",
+        "no-keys-found": ".هيچ کليدي يافت نشد",
+        "no-key-matching": "'.يافت نشد {{key}}'",
+        "create-new-key": "!ايجاد يک کليد جديد",
+        "type": "نوع",
+        "type-required": ".نوع موجودي مورد نياز است",
+        "type-device": "دستگاه",
+        "type-devices": "دستگاه ها",
+        "list-of-devices": "{ count, plural, 1 {يک دستگاه} other {ليست # دستگاه} }",
+        "device-name-starts-with": "شروع مي شود '{{prefix}}' دستگاه هايي که نامشان با",
+        "type-asset": "دارايي",
+        "type-assets": "دارايي ها",
+        "list-of-assets": "{ count, plural, 1 {يک دارايي} other {ليست # دارايي} }",
+        "asset-name-starts-with": "شروع مي شود '{{prefix}}' دارايي هايي که نامشان با",
+        "type-entity-view": "نمايش موجودي",
+        "type-entity-views": "نمايش هاي موجودي",
+        "list-of-entity-views": "{ count, plural, 1 {يک نمايش موجودي} other {ليست # نمايش موجودي} }",
+        "entity-view-name-starts-with": "شروع مي شود '{{prefix}}' نمايش هاي موجودي که نامشان با",
+        "type-rule": "قاعده",
+        "type-rules": "قواعد",
+        "list-of-rules": "{ count, plural, 1 {يک قاعده} other {ليست # قاعده} }",
+        "rule-name-starts-with": "شروع مي شود '{{prefix}}' قواعدي که نامشان با",
+        "type-plugin": "ابزار جانبي",
+        "type-plugins": "ابزارهاي جانبي",
+        "list-of-plugins": "{ count, plural, 1 {يک ابزار جانبي} other {ليست # ابزار جانبي} }",
+        "plugin-name-starts-with": "شروع مي شود '{{prefix}}' ابزارهاي جانبي که نامشان با",
+        "type-tenant": "کاربر",
+        "type-tenants": "کاربران",
+        "list-of-tenants": "{ count, plural, 1 {يک کاربر} other {ليست # کاربر} }",
+        "tenant-name-starts-with": "شروع مي شود '{{prefix}}' کاربرهايي که نامشان با",
+        "type-customer": "مشتري",
+        "type-customers": "مشتريان",
+        "list-of-customers": "{ count, plural, 1 {يک مشتري} other {ليست # مشتري} }",
+        "customer-name-starts-with": "شروع مي شود '{{prefix}}' مشترياني که نامشان با",
+        "type-user": "کاربر",
+        "type-users": "کاربران",
+        "list-of-users": "{ count, plural, 1 {يک کاربر} other {ليست # کاربر} }",
+        "user-name-starts-with": "شروع مي شود '{{prefix}}' کاربرهايي که نامشان با",
+        "type-dashboard": "داشبورد",
+        "type-dashboards": "داشبوردها",
+        "list-of-dashboards": "{ count, plural, 1 {يک داشبورد} other {ليست # داشبورد} }",
+        "dashboard-name-starts-with": "شروع مي شود '{{prefix}}' داشبوردهايي که نامشان با",
+        "type-alarm": "هشدار",
+        "type-alarms": "هشدارها",
+        "list-of-alarms": "{ count, plural, 1 {يک هشدار} other {ليست # هشدار} }",
+        "alarm-name-starts-with": "شروع مي شود '{{prefix}}' هشدارهايي که نامشان با",
+        "type-rulechain": "زنجيره قواعد",
+        "type-rulechains": "زنجيره هاي قواعد",
+        "list-of-rulechains": "{ count, plural, 1 {يک زنجيره قواعد} other {ليست # زنجيره قواعد} }",
+        "rulechain-name-starts-with": "شروع مي شود '{{prefix}}' زنجيره هاي قواعدي که نامشان با",
+        "type-rulenode": "گره قواعد",
+        "type-rulenodes": "گره هاي قواعد",
+        "list-of-rulenodes": "{ count, plural, 1 {يک گره قواعد} other {ليست # گره قواعد} }",
+        "rulenode-name-starts-with": "شروع مي شود '{{prefix}}' گره هاي قواعدي که نامشان با",
+        "type-current-customer": "مشتري فعلي",
+        "search": "جستجوي موجودي ها",
+        "selected-entities": "انتخاب شدند { count, plural, 1 {1 موجودي} other {# موجودي} }",
+        "entity-name": "نام موجودي",
+        "details": "جزئيات موجودي",
+        "no-entities-prompt": "هيچ موجودي اي يافت نشد",
+        "no-data": "هيچ داده اي براي نمايش نيست",
+        "columns-to-display": "ستون ها براي نمايش"
+    },
+    "entity-view": {
+        "entity-view": "نمايش موجودي",
+        "entity-view-required": ".نمايش موجودي مورد نياز است",
+        "entity-views": "نمايش هاي موجودي",
+        "management": "مديريت نمايش موجودي",
+        "view-entity-views": "نمايش نمايش هاي موجودي",
+        "entity-view-alias": "نام مستعار نمايش موجودي",
+        "aliases": "نامهاي مستعار نمايش موجودي",
+        "no-alias-matching": ".يافت نشد '{{alias}}'",
+        "no-aliases-found": ".هيچ نام مستعاري يافت نشد",
+        "no-key-matching": ".يافت نشد '{{key}}'",
+        "no-keys-found": ".هيچ کليدي يافت نشد",
+        "create-new-alias": "!ايجاد نام مستعار جديد",
+        "create-new-key": "!ايجاد کليد جديد",
+        "duplicate-alias-error": ".نامهاي مستعار نمايش موجودي بايد در داشبورد، منحصر بفرد باشند<br>.يافت شد '{{alias}}' نام مستعار تکراري",
+        "configure-alias": "'{{alias}}' پيکربندي نام مستعار",
+        "no-entity-views-matching": ".يافت نشد '{{entity}}' هيچ موجودي منطبق بر",
+        "alias": "نام مستعار",
+        "alias-required": ".نام مستعار نمايش موجودي مورد نياز است",
+        "remove-alias": "حذف نام مستعار نمايش موجودي",
+        "add-alias": "افزودن نام مستعار نمايش موجودي",
+        "name-starts-with": "نام نمايش موجودي شروع مي شود با",
+        "entity-view-list": "ليست نمايش موجودي",
+        "use-entity-view-name-filter": "استفاده از فيلتر",
+        "entity-view-list-empty": ".هيچ نمايش موجودي انتخاب نشد",
+        "entity-view-name-filter-required": ".فيلتر نام نمايش موجودي مورد نياز است",
+        "entity-view-name-filter-no-entity-view-matched": ".شروع شود يافت نشد '{{entityView}}' هيچ نمايش موجودي که با",
+        "add": "افزودن نمايش موجودي",
+        "assign-to-customer": "تخصيص به مشتري",
+        "assign-entity-view-to-customer": "تخصيص نمايش(هاي) موجودي به مشتري",
+        "assign-entity-view-to-customer-text": ".لطفا نمايش هاي موجودي را انتخاب کنيد تا به مشتري تخصيص يابند",
+        "no-entity-views-text": "هيچ نمايش موجودي يافت نشد",
+        "assign-to-customer-text": ".لطفا مشتري را انتخاب کنيد تا نمايش(هاي) موجودي تخصيص يابد",
+        "entity-view-details": "جزئيات نمايش موجودي",
+        "add-entity-view-text": "افزودن نمايش موجودي جديد",
+        "delete": "حذف نمايش موجودي",
+        "assign-entity-views": "تخصيص نمايش هاي موجودي",
+        "assign-entity-views-text": "به مشتري { count, plural, 1 {1 نمايش موجودي} other {# نمايش موجودي} } تخصيص",
+        "delete-entity-views": "حذف نمايش هاي موجودي",
+        "unassign-from-customer": "لغو تخصيص از مشتري",
+        "unassign-entity-views": "لغو تخصيص نمايش هاي موجودي",
+        "unassign-entity-views-action-title": "از مشتري { count, plural, 1 {1 نمايش موجودي} other {# نمايش موجودي} } لغو تخصيص",
+        "assign-new-entity-view": "تخصيص نمايش موجودي جديد",
+        "delete-entity-view-title": "مطمئنيد؟ '{{entityViewName}}' از حذف نمايش موجودي",
+        "delete-entity-view-text": ".مراقب باشيد، پس از تأييد، نمايش موجودي و تمامي داده هاي مربوطه، غير قابل بازيابي مي شوند",
+        "delete-entity-views-title": "مطمئنيد؟ { count, plural, 1 {1 نمايش موجودي} other {# نمايش موجودي} } از حذف نمايش موجودي",
+        "delete-entity-views-action-title": "{ count, plural, 1 {1 نمايش موجودي} other {# نمايش موجودي} } حذف",
+        "delete-entity-views-text": ".مراقب باشيد، پس از تأييد، تمام نمايش هاي موجوديِ انتخاب شده حذف، و تمامي داده هاي مربوطه غير قابل بازيابي مي شوند",
+        "unassign-entity-view-title": "مطمئنيد؟ '{{entityViewName}}' از لغو تخصيص نمايش موجودي",
+        "unassign-entity-view-text": ".پس از تأييد، نمايش موجودي، لغو تخصيص و خارج از دسترس مشتري مي شود",
+        "unassign-entity-view": "لغو تخصيص نمايش موجودي",
+        "unassign-entity-views-title": "مطمئنيد؟ { count, plural, 1 {1 نمايش موجودي} other {# نمايش موجودي} } از لغو تخصيص",
+        "unassign-entity-views-text": ".پس از تأييد، تمام نمايش هاي موجوديِ انتخاب شده، لغو تخصيص و خارج از دسترس مشتري مي شوند",
+        "entity-view-type": "نوع نمايش موجودي",
+        "entity-view-type-required": ".نوع نمايش موجودي مورد نياز است",
+        "select-entity-view-type": "انتخاب نوع نمايش موجودي",
+        "enter-entity-view-type": "وارد کردن نوع نمايش موجودي",
+        "any-entity-view": "هر نمايش موجودي",
+        "no-entity-view-types-matching": ".يافت نشد '{{entitySubtype}}' هيچ نوع نمايش موجودي منطبق بر",
+        "entity-view-type-list-empty": ".هيچ نوع نمايش موجودي انتخاب نشد",
+        "entity-view-types": "انواع نمايش موجودي",
+        "name": "نام",
+        "name-required": ".نام مورد نياز است",
+        "description": "توصيف",
+        "events": "رويدادها",
+        "details": "جزئيات",
+        "copyId": "نمايش موجودي ID رونوشت از",
+        "assignedToCustomer": "تخصيص يافته به مشتري",
+        "unable-entity-view-device-alias-title": ".حذف نام مستعار نمايش موجودي ممکن نيست",
+        "unable-entity-view-device-alias-text": "<br/>{{widgetsList}} :را تا زمان استفاده توسط ويجت(هاي) زير نمي توان حذف کرد ، '{{entityViewAlias}}' ،نام مستعار دستگاه",
+        "select-entity-view": "انتخاب نمايش موجودي",
+        "make-public": "عمومي سازي نمايش موجودي",
+        "start-date": "تاريخ شروع",
+        "start-ts": "زمان شروع",
+        "end-date": "تاريخ پايان",
+        "end-ts": "زمان پايان",
+        "date-limits": "محدوده تاريخ",
+        "client-attributes": "ويژگي هاي مشتري",
+        "shared-attributes": "ويژگي هاي مشترک",
+        "server-attributes": "ويژگي هاي سِروِر",
+        "timeseries": "سري هاي زماني",
+        "client-attributes-placeholder": "ويژگي هاي مشتري",
+        "shared-attributes-placeholder": "ويژگي هاي مشترک",
+        "server-attributes-placeholder": "ويژگي هاي سِروِر",
+        "timeseries-placeholder": "سري هاي زماني",
+        "target-entity": "موجودي هدف",
+        "attributes-propagation": "انتشار ويژگي ها",
+        "attributes-propagation-hint": "هر بار که شما نمايش موجودي را بروز رساني يا ذخيره مي کنيد، نمايش موجودي بصور خودکار ويژگي هاي تعيين شده را از موجودي هدف کپي مي کند و به دلايل عملکردي، ويژگي هاي موجودي هدف، با هر بار تغيير ويژگي، در نمايش موجودي انتشار نمي يابند. مي توانيد با پيکربندي گره قواعد در زنجيره قواعد خود و پيوند دهي \"Post attributes\"  و \"Attributes Updated\"  آن به گره قواعد جديد، انتشار خودکار \"copy to view\"  را ممکن سازيد ." ,
+        "timeseries-data": "داده ي سري هاي زماني",
+        "timeseries-data-hint": "کليدهاي داده ي سري هاي زمانيِ موجوديِ هدف را پيکربندي کنيد تا در دسترسِ نمايش موجودي باشند. اين سري هاي زماني، فقط خواندني است"
+    },
+    "event": {
+        "event-type": "نوع رويداد",
+        "type-error": "خطا",
+        "type-lc-event": "رويداد چرخه عمر",
+        "type-stats": "آمار",
+        "type-debug-rule-node": "اشکال زدايي",
+        "type-debug-rule-chain": "اشکال زدايي",
+        "no-events-prompt": "هيچ رويدادي يافت نشد",
+        "error": "خطا",
+        "alarm": "هشدار",
+        "event-time": "زمان رويداد",
+        "server": "سِروِر",
+        "body": "بدنه",
+        "method": "روش",
+        "type": "نوع",
+        "entity": "موجودي",
+        "message-id": "پيام ID",
+        "message-type": "نوع پيام",
+        "data-type": "نوع داده",
+        "relation-type": "نوع ارتباط",
+        "metadata": "فرا داده",
+        "data": "داده",
+        "event": "رويداد",
+        "status": "وضعيت",
+        "success": "موفقيت",
+        "failed": "عدم موفقيت",
+        "messages-processed": "پيام پردازش شد",
+        "errors-occurred": "خطاها رخ دادند"
+    },
+    "extension": {
+        "extensions": "دنباله ها",
+        "selected-extensions": "انتخاب شدند { count, plural, 1 {1 افزونه} other {افزونه ها #} }",
+        "type": "نوع",
+        "key": "کليد",
+        "value": "مقدار",
+        "id": "ID",
+        "extension-id": " افزونه ID",
+        "extension-type": "نوع افزونه",
+        "transformer-json": "JSON *",
+        "unique-id-required": ".افزونه فعلي موجود است ID در حال حاضر",
+        "delete": "حذف دنباله",
+        "add": "افزودن دنباله",
+        "edit": "ويرايش دنباله",
+        "delete-extension-title": "مطمئنيد؟ '{{extensionId}}' از حذف افزونه",
+        "delete-extension-text": ".مراقب باشيد، پس از تأييد، افزونه و تمامي داده هاي مربوطه غير قابل بازيابي مي شوند",
+        "delete-extensions-title": "مطمئنيد؟ { count, plural, 1 {1 افزونه} other {# افزونه} } از حذف",
+        "delete-extensions-text": ".مراقب باشيد، پس از تأييد، تمام افزونه هاي انتخاب شده حذف مي گردند",
+        "converters": "مبدّل ها",
+        "converter-id": "مبدّل ID",
+        "configuration": "پيکربندي",
+        "converter-configurations": "پيکربندي هاي مبدّل",
+        "token": "نشانه امنيت",
+        "add-converter": "افزودن مبدّل",
+        "add-config": "افزودن پيکربندي مبدّل",
+        "device-name-expression": "عبارت نام دستگاه",
+        "device-type-expression": "عبارت نوع دستگاه",
+        "custom": "متداول",
+        "to-double": "دو برابر شدن",
+        "transformer": "مبدّل",
+        "json-required": ".مبدّل مورد نياز است JSON",
+        "json-parse": ".مبدّل ممکن نيست JSON تجزيه",
+        "attributes": "ويژگي ها",
+        "add-attribute": "افزودن ويژگي",
+        "add-map": "افزودن جزء نگاشت",
+        "timeseries": "سري هاي زماني",
+        "add-timeseries": "افزودن سري هاي زماني",
+        "field-required": "دامنه مورد نياز است",
+        "brokers": "واسطه ها",
+        "add-broker": "افزودن واسطه",
+        "host": "ميزبان",
+        "port": "درگاه",
+        "port-range": ".درگاه بايد در بازه اي بين 1 تا 65535 باشد",
+        "ssl": "Ssl",
+        "credentials": "اعتبارنامه ها",
+        "username": "نام کاربري",
+        "password": "رمز عبور",
+        "retry-interval": "بازخواني فاصله در ميلي ثانيه",
+        "anonymous": "بي نام",
+        "basic": "پايه",
+        "pem": "PEM",
+        "ca-cert": "CA پرونده گواهينامه *",
+        "private-key": "پرونده کليد شخصي *",
+        "cert": "پرونده گواهينامه *",
+        "no-file": ".هيچ پرونده اي انتخاب نشد",
+        "drop-file": ".جهت بارگذاري يک پرونده، آن را با موس کِشيده و رها کنيد، و يا روي آن کليک نماييد",
+        "mapping": "نگاشت",
+        "topic-filter": "فيلتر عنوان",
+        "converter-type": "نوع مبدّل",
+        "converter-json": "JSON",
+        "json-name-expression": "نام دستگاه JSON عبارت",
+        "topic-name-expression": "عبارت عنوان نام دستگاه",
+        "json-type-expression": "نوع دستگاه JSON عبارت",
+        "topic-type-expression": "عبارت عنوان نوع دستگاه",
+        "attribute-key-expression": "عبارت کليد ويژگي",
+        "attr-json-key-expression": "کليد ويژگي JSON عبارت",
+        "attr-topic-key-expression": "عبارت عنوان کليد ويژگي",
+        "request-id-expression": "ID درخواست عبارت",
+        "request-id-json-expression": "ID JSON درخواست عبارت",
+        "request-id-topic-expression": "ID درخواست عبارت عنوان",
+        "response-topic-expression": "عبارت عنوان پاسخ",
+        "value-expression": "عبارت مقدار",
+        "topic": "عنوان",
+        "timeout": "وقفه در ميلي ثانيه",
+        "converter-json-required": ".مبدّل مورد نياز است JSON",
+        "converter-json-parse": ".مبدّل ممکن نيست JSON تجزيه",
+        "filter-expression": "عبارت فيلتر",
+        "connect-requests": "درخواست هاي اتصال",
+        "add-connect-request": "افزودن درخواست اتصال",
+        "disconnect-requests": "درخواست هاي قطع اتصال",
+        "add-disconnect-request": "افزودن درخواست قطع اتصال",
+        "attribute-requests": "درخواست هاي ويژگي",
+        "add-attribute-request": "افزودن درخواست ويژگي",
+        "attribute-updates": "به روز رساني هاي ويژگي ",
+        "add-attribute-update": "افزودن به روز رساني ويژگي ",
+        "server-side-rpc": "سَمت سِروِر RPC",
+        "add-server-side-rpc-request": "سَمت سِروِر RPC افزودن درخواست",
+        "device-name-filter": "فيلتر نام دستگاه",
+        "attribute-filter": "فيلتر ويژگي ",
+        "method-filter": "فيلتر روش",
+        "request-topic-expression": "عبارت عنوان درخواست",
+        "response-timeout": "وقفه پاسخ در ميلي ثانيه",
+        "topic-expression": "بيان موضوع",
+        "client-scope": "حوزه مشتري",
+        "add-device": "افزودن دستگاه",
+        "opc-server": "سِروِرها",
+        "opc-add-server": "افزودن سِروِر",
+        "opc-add-server-prompt": "لطفا سِروِر را اضافه کنيد",
+        "opc-application-name": "نام برنامه کاربردي",
+        "opc-application-uri": "برنامه کاربردي URI",
+        "opc-scan-period-in-seconds": "دوره پويش در ثانيه",
+        "opc-security": "امنيت",
+        "opc-identity": "هويت",
+        "opc-keystore": "کي استور",
+        "opc-type": "نوع",
+        "opc-keystore-type": "نوع",
+        "opc-keystore-location": "* موقعيت مکاني",
+        "opc-keystore-password": "رمز عبور",
+        "opc-keystore-alias": "نام مستعار",
+        "opc-keystore-key-password": "کليد رمز عبور",
+        "opc-device-node-pattern": "الگوي گره دستگاه",
+        "opc-device-name-pattern": "الگوي نام دستگاه",
+        "modbus-server": "سِروِرها/جايگزين آماده به کار",
+        "modbus-add-server": "افزودن سِروِر/ جايگزين آماده به کار ",
+        "modbus-add-server-prompt": "لطفا سِروِرها/جايگزين آماده به کار را اضافه کنيد",
+        "modbus-transport": "انتقال",
+        "modbus-port-name": "نام در گاه سريال",
+        "modbus-encoding": "رمز گذاري",
+        "modbus-parity": "توازن",
+        "modbus-baudrate": "نرخ علامت در ثانيه",
+        "modbus-databits": "بيت هاي داده",
+        "modbus-stopbits": "بيت هاي توقف",
+        "modbus-databits-range": ".بيت هاي داده بايد در بازه اي بين 7 تا 8 باشند",
+        "modbus-stopbits-range": ".بيت هاي توقف بايد در بازه اي بين 1 تا 2 باشند",
+        "modbus-unit-id": "واحد ID",
+        "modbus-unit-id-range": ".واحد بايد در بازه اي بين 1 تا 247 باشد ID",
+        "modbus-device-name": "نام دستگاه",
+        "modbus-poll-period": "(ms) دوره نمونه برداري",
+        "modbus-attributes-poll-period": "(ms) دوره نمونه برداري ويژگي ها",
+        "modbus-timeseries-poll-period": "(ms) دوره نمونه برداري سري هاي زماني",
+        "modbus-poll-period-range": ".دوره نمونه برداري بايد مقداري مثبت باشد",
+        "modbus-tag": "برچسب",
+        "modbus-function": "تابع",
+        "modbus-register-address": "ثبت نام نشاني",
+        "modbus-register-address-range": ".نشاني ثبت بايد در بازه اي بين 0 تا 65535 باشد",
+        "modbus-register-bit-index": "شاخص بيت",
+        "modbus-register-bit-index-range": ".شاخص بيت بايد در بازه اي بين 0 تا 15 باشد",
+        "modbus-register-count": "شمارش ثبت",
+        "modbus-register-count-range": ".شمارش ثبت بايد مقداري مثبت باشد",
+        "modbus-byte-order": "ترتيب بايت",
+
+        "sync": {
+            "status": "وضعيت",
+            "sync": "همگام",
+            "not-sync": "غير همگام",
+            "last-sync-time": "آخرين زمان همگام سازي",
+            "not-available": "خارج از دسترس"
+        },
+
+        "export-extensions-configuration": "صدور پيکربندي افزونه ها",
+        "import-extensions-configuration": "وارد کردن پيکربندي افزونه ها",
+        "import-extensions": "وارد کردن افزونه ها",
+        "import-extension": "وارد کردن افزونه",
+        "export-extension": "صدور افزونه",
+        "file": "پرونده افزونه ها",
+        "invalid-file-error": "پرونده افزونه نامعتبر است"
+    },
+    "fullscreen": {
+        "expand": "بسط به حالت تمام صفحه",
+        "exit": "خروج از حالت تمام صفحه",
+        "toggle": "تغيير حالت تمام صفحه",
+        "fullscreen": "حالت تمام صفحه"
+    },
+    "function": {
+        "function": "تابع"
+    },
+    "grid": {
+        "delete-item-title": "از حذف اين مورد مطمئنيد؟",
+        "delete-item-text": ".مراقب باشيد، پس از تأييد، اين مورد و تمامي داده هاي مربوطه غيرقابل بازيابي مي شوند",
+        "delete-items-title": "مطمئنيد؟ { count, plural, 1 {1 مورد} other {# مورد} } از حذف",
+        "delete-items-action-title": "{ count, plural, 1 {1 مورد} other {# مورد} } حذف",
+        "delete-items-text": ".مراقب باشيد، پس از تأييد، تمام مواردِ انتخاب شده حذف، و تمامي داده هاي مربوطه غير قابل بازيابي مي شوند",
+        "add-item-text": "افزودن مورد جديد",
+        "no-items-text": "هيچ موردي يافت نشد",
+        "item-details": "جزئيات مورد",
+        "delete-item": "حذف مورد",
+        "delete-items": "حذف موارد",
+        "scroll-to-top": "پيمايش به بالا"
+    },
+    "help": {
+        "goto-help-page": "رفتن به صفحه کمک"
+    },
+    "home": {
+        "home": "خانه",
+        "profile": "پرونده شخصي",
+        "logout": "خروج",
+        "menu": "فهرست انتخاب",
+        "avatar": "آواتار",
+        "open-user-menu": "بازکردن فهرست انتخاب کاربر"
+    },
+    "import": {
+        "no-file": "هيچ پرونده اي انتخاب نشد",
+        "drop-file": ".آن را با موس کِشيده و رها کنيد، و يا روي آن کليک نماييد ،JSON جهت بارگذاري يک پرونده"
+    },
+    "item": {
+        "selected": "انتخاب شده"
+    },
+    "js-func": {
+        "no-return-error": "!تابع بايد مقدار را برگرداند",
+        "return-type-mismatch": "!را برگرداند '{{type}}' تابع بايد مقدار نوع",
+        "tidy": "مرتب"
+    },
+    "key-val": {
+        "key": "کليد",
+        "value": "مقدار",
+        "remove-entry": "حذف ورودي",
+        "add-entry": "افزودن ورودي",
+        "no-data": "هيچ ورودي وجود ندارد"
+    },
+    "layout": {
+        "layout": "طرح بندي",
+        "manage": "مديريت طرح بندي ها",
+        "settings": "تنظيمات طرح بندي",
+        "color": "رنگ",
+        "main": "اصلي",
+        "right": "راست",
+        "select": "انتخاب طرح بندي هدف"
+    },
+    "legend": {
+        "position": "محل فهرست علائم",
+        "show-max": "نمايش بيشترين مقدار",
+        "show-min": "نمايش کمترين مقدار",
+        "show-avg": "نمايش مقدار ميانگين",
+        "show-total": "نمايش مقدار مجموع",
+        "settings": "تنظيمات فهرست علائم",
+        "min": "کمترين",
+        "max": "بيشترين",
+        "avg": "ميانگين",
+        "total": "مجموع"
+    },
+    "login": {
+        "login": "ورود",
+        "request-password-reset": "درخواست بازنشاني رمز عبور",
+        "reset-password": "بازنشاني رمز عبور",
+        "create-password": "ايجاد رمز عبور",
+        "passwords-mismatch-error": "!رمزهاي عبور وارد شده بايد مشابه باشند",
+        "password-again": "رمز عبور دوباره",
+        "sign-in": "لطفا وارد شويد",
+        "username": "(نام کاربري (پست الکترونيک",
+        "remember-me": "مرا به خاطر داشته باش",
+        "forgot-password": "رمز عبور را فراموش کرده ايد؟",
+        "password-reset": "بازنشاني رمز عبور",
+        "new-password": "رمز عبور جديد",
+        "new-password-again": "رمز عبور جديد دوباره",
+        "password-link-sent-message": "!پيوند بازنشاني رمز عبور با موفقيت ارسال شد",
+        "email": "پست الکترونيک"
+    },
+    "position": {
+        "top": "بالا",
+        "bottom": "پايين",
+        "left": "چپ",
+        "right": "راست"
+    },
+    "profile": {
+        "profile": "پرونده شخصي",
+        "change-password": "تغيير رمز عبور",
+        "current-password": "رمز عبور فعلي"
+    },
+    "relation": {
+        "relations": "ارتباطات",
+        "direction": "جهت",
+        "search-direction": {
+            "FROM": "از",
+            "TO": "به"
+        },
+        "direction-type": {
+            "FROM": "از",
+            "TO": "به"
+        },
+        "from-relations": "ارتباطات خارج از محدوده",
+        "to-relations": "ارتباطات داخل محدوده",
+        "selected-relations": "انتخاب شدند { count, plural, 1 {1 ارتباط} other {ارتباط #} }",
+        "type": "نوع",
+        "to-entity-type": "به نوع موجودي",
+        "to-entity-name": "به نام موجودي",
+        "from-entity-type": "از نوع موجودي",
+        "from-entity-name": "از نام موجودي",
+        "to-entity": "به موجودي",
+        "from-entity": "از موجودي",
+        "delete": "حذف ارتباط",
+        "relation-type": "نوع ارتباط",
+        "relation-type-required": ".نوع ارتباط مورد نياز است",
+        "any-relation-type": "هر نوع",
+        "add": "افزودن ارتباط",
+        "edit": "ويرايش ارتباط",
+        "delete-to-relation-title": "مطمئنيد؟ '{{entityName}}' از حذف ارتباط با موجودي",
+        "delete-to-relation-text": ".غيرمرتبط با موجودي فعلي مي شود '{{entityName}}' مراقب باشيد، پس از تأييد، موجودي",
+        "delete-to-relations-title": "مطمئنيد؟ { count, plural, 1 {1 ارتباط} other {# ارتباط} } از حذف",
+        "delete-to-relations-text": ".مراقب باشيد، پس از تأييد، تمام روابطِ انتخاب شده حذف، و موجودي هاي مربوطه، غيرمرتبط با موجودي فعلي مي شوند",
+        "delete-from-relation-title": "مطمئنيد؟ '{{entityName}}' از حذف ارتباط از موجودي",
+        "delete-from-relation-text": ".مي شود '{{entityName}}' مراقب باشيد، پس از تأييد، موجودي فعلي، غيرمرتبط از جانب موجودي",
+        "delete-from-relations-title": "مطمئنيد؟ { count, plural, 1 {1 ارتباط} other {# ارتباط} } از حذف",
+        "delete-from-relations-text": ".مراقب باشيد، پس از تأييد، تمام روابطِ انتخاب شده حذف، و موجودي فعلي، غيرمرتبط از جانب موجودي هاي مربوطه مي شود",
+        "remove-relation-filter": "حذف فيلتر ارتباط",
+        "add-relation-filter": "افزودن فيلتر ارتباط",
+        "any-relation": "هر ارتباط",
+        "relation-filters": "فيلترهاي ارتباط",
+        "additional-info": "(JSON) اطلاعات تکميلي",
+        "invalid-additional-info": "اطلاعات تکميلي ممکن نيست JSON تجزيه"
+    },
+    "rulechain": {
+        "rulechain": "زنجيره قواعد",
+        "rulechains": "زنجيره هاي قواعد",
+        "root": "پايه",
+        "delete": "حذف زنجيره قواعد",
+        "name": "نام",
+        "name-required": ".نام مورد نياز است",
+        "description": "توصيف",
+        "add": "افزودن زنجيره قواعد",
+        "set-root": "ريشه زنجيره قواعد را ايجاد کنيد",
+        "set-root-rulechain-title": "به عنوان ريشه مطمئنيد؟ '{{ruleChainName}}' از قرار دادن زنجيره قواعد",
+        "set-root-rulechain-text": ".پس از تأييد، زنجيره قواعد، به عنوان ريشه تعيين شده و به تمام پيامهاي انتقالي رسيدگي مي کند",
+        "delete-rulechain-title": "مطمئنيد؟ '{{ruleChainName}}' از حذف زنجيره قواعد",
+        "delete-rulechain-text": ".مراقب باشيد، پس از تأييد، زنجيره قواعد و تمامي داده هاي مربوطه غير قابل بازيابي مي شوند",
+        "delete-rulechains-title": "مطمئنيد؟ { count, plural, 1 {1 زنجيره قواعد} other {# زنجيره قواعد} } از حذف",
+        "delete-rulechains-action-title": "{ count, plural, 1 {1 زنجيره قواعد} other {# زنجيره قواعد} } حذف",
+        "delete-rulechains-text": ".مراقب باشيد، پس از تأييد، تمام زنجيره هاي قواعدِ انتخاب شده حذف، و تمامي داده هاي مربوطه غير قابل بازيابي مي شوند",
+        "add-rulechain-text": "افزودن زنجيره قواعد جديد",
+        "no-rulechains-text": "هيچ زنجيره قواعدي يافت نشد",
+        "rulechain-details": "جزئيات زنجيره قواعد",
+        "details": "جزئيات",
+        "events": "رويدادها",
+        "system": "سيستم",
+        "import": "وارد کردن زنجيره قواعد",
+        "export": "صدور زنجيره قواعد",
+        "export-failed-error": "{{error}} :صدور زنجيره قواعد ممکن نيست",
+        "create-new-rulechain": "ايجاد زنجيره قواعد جديد",
+        "rulechain-file": "پرونده زنجيره قواعد",
+        "invalid-rulechain-file-error": ".وارد کردن زنجيره قواعد ممکن نيست: ساختار داده زنجيره قواعد نامعتبر است",
+        "copyId": "زنجيره قواعد ID رونوشت",
+        "idCopiedMessage": "زنجيره قواعد در حافظه موقت رونوشت شد ID",
+        "select-rulechain": "انتخاب زنجيره قواعد",
+        "no-rulechains-matching": ".يافت نشد '{{entity}}' هيچ زنجيره قواعدي منطبق بر",
+        "rulechain-required": "زنجيره قواعد مورد نياز است",
+        "management": "مديريت قواعد",
+        "debug-mode": "حالت اشکال زدايي"
+    },
+    "rulenode": {
+        "details": "جزئيات",
+        "events": "رويدادها",
+        "search": "جستجوي گره ها",
+        "open-node-library": "باز کردن کتابخانه گره ها",
+        "add": "افزودن گره قواعد",
+        "name": "نام",
+        "name-required": ".نام مورد نياز است",
+        "type": "نوع",
+        "description": "توصيف",
+        "delete": "حذف گره قواعد",
+        "select-all-objects": "انتخاب تمام گره ها و اتصالات",
+        "deselect-all-objects": "لغو انتخاب تمام گره ها و اتصالات",
+        "delete-selected-objects": "حذف گره ها و اتصالاتِ انتخاب شده",
+        "delete-selected": "حذفِ انتخاب شده",
+        "select-all": "انتخاب همه",
+        "copy-selected": "رونوشتِ انتخاب شده",
+        "deselect-all": "لغو انتخاب همه",
+        "rulenode-details": "جزئيات گره قواعد",
+        "debug-mode": "حالت اشکال زدايي",
+        "configuration": "پيکربندي",
+        "link": "پيوند",
+        "link-details": "جزئيات پيوند گره قواعد",
+        "add-link": "افزودن پيوند",
+        "link-label": "برچسب پيوند",
+        "link-label-required": ".برچسب پيوند مورد نياز است",
+        "custom-link-label": "برچسب پيوند متداول",
+        "custom-link-label-required": ".برچسب پيوند متداول مورد نياز است",
+        "link-labels": "برچسب هاي پيوند",
+        "link-labels-required": ".برچسب هاي پيوند مورد نيازند",
+        "no-link-labels-found": "هيچ برچسب پيوندي يافت نشد",
+        "no-link-label-matching": ".پيدا نشد'{{label}}'",
+        "create-new-link-label": "!برچسب پيوند ايجاد کنيد",
+        "type-filter": "فيلتر",
+        "type-filter-details": "پيام هاي ورودي را با شرايط پيکربندي فيلتر کنيد",
+        "type-enrichment": "افزودن",
+        "type-enrichment-details": "افزودن اطلاعات تکميلي به فرا داده ي پيام",
+        "type-transformation": "تبديل",
+        "type-transformation-details": "تغيير بازده و فرا داده ي پيام",
+        "type-action": "اقدام",
+        "type-action-details": "انجام اقدام ويژه",
+        "type-external": "خارجي",
+        "type-external-details": "ارتباط متقابل با سيستم خارجي",
+        "type-rule-chain": "زنجيره قواعد",
+        "type-rule-chain-details": "ارسال پيامهاي وارده به زنجيره قواعدي مشخص",
+        "type-input": "ورودي",
+        "type-input-details": "ورودي منطقي زنجيره قواعد، پيامهاي ورودي را به گره قواعد مرتبط بعدي ارسال مي کند",
+        "type-unknown": "ناشناخته",
+        "type-unknown-details": "گره قواعدِ حل نشده",
+        "directive-is-not-loaded": ".در دسترس نيست '{{directiveName}}' دستورالعمل پيکربنديِ مشخص شده",
+        "ui-resources-load-error": ".پيکربندي UI عدم موفقيت در بارگذاري منابع",
+        "invalid-target-rulechain": "!حلّ زنجيره قواعد هدف ممکن نيست",
+        "test-script-function": "آزمايش تابع اسکريپت",
+        "message": "پيام",
+        "message-type": "نوع پيام",
+        "select-message-type": "انتخاب نوع پيام",
+        "message-type-required": "نوع پيام مورد نياز است",
+        "metadata": "فوق داده",
+        "metadata-required": ".ورودي هاي فرا داده نمي تواند خالي باشد",
+        "output": "خروجي",
+        "test": "آزمايش",
+        "help": "کمک"
+    },
+        "tenant": {
+        "tenant": "کاربر",
+        "tenants": "کاربران",
+        "management": "مديريت کاربران",
+        "add": "افزودن کاربر",
+        "admins": "سرپرستان",
+        "manage-tenant-admins": "مديريت مديران کاربر",
+        "delete": "حذف کاربر",
+        "add-tenant-text": "افزودن کاربر جديد",
+        "no-tenants-text": "هيچ کاربري يافت نشد",
+        "tenant-details": "جزئيات کاربر",
+        "delete-tenant-title": "مطمئنيد؟ '{{tenantTitle}}' از حذف کاربر",
+        "delete-tenant-text": ".مراقب باشيد، پس از تأييد، کاربر و تمامي داده هاي مربوطه غير قابل بازيابي مي شوند",
+        "delete-tenants-title": "مطمئنيد؟ { count, plural, 1 {1 کاربر} other {# کاربر} } از حذف",
+        "delete-tenants-action-title": "{ count, plural, 1 {1 کاربر} other {# کاربر} } حذف",
+        "delete-tenants-text": ".مراقب باشيد، پس از تأييد، تمام کاربران حذف، و تمامي داده هاي مربوطه غير قابل بازيابي مي شوند",
+        "title": "عنوان",
+        "title-required": ".عنوان مورد نياز است",
+        "description": "توصيف",
+        "details": "جزئيات",
+        "events": "رويدادها",
+        "copyId": "متصرّف ID رونوشت",
+        "idCopiedMessage": "کاربر در حافظه موقت رونوشت شد ID",
+        "select-tenant": "انتخاب کاربر",
+        "no-tenants-matching": ".يافت نشد '{{entity}}' هيچ کاربري منطبق بر",
+        "tenant-required": "کاربر مورد نياز است"
+    },
+    "timeinterval": {
+        "seconds-interval": "{ seconds, plural, 1 {1 ثانيه} other {ثانيه #} }",
+        "minutes-interval": "{ minutes, plural, 1 {1 دقيقه} other {دقيقه #} }",
+        "hours-interval": "{ hours, plural, 1 {1 ساعت} other {ساعت #} }",
+        "days-interval": "{ days, plural, 1 {1 روز} other {روز #} }",
+        "days": "روزها",
+        "hours": "ساعات",
+        "minutes": "دقايق",
+        "seconds": "ثانيه ها",
+        "advanced": "پيشرفته"
+    },
+    "timewindow": {
+        "days": "{ days, plural, 1 { روز } other {روز #} }",
+        "hours": "{ hours, plural, 0 { 1ساعت } 1 { ساعت } other {# ساعت } }",
+        "minutes": "{ minutes, plural, 0 { 1دقيقه } 1 { دقيقه } other {دقيقه # } }",
+        "seconds": "{ seconds, plural, 0 { 1ثانيه } 1 { ثانيه } other {ثانيه # } }",
+        "realtime": "بي درنگ",
+        "history": "تاريخچه",
+        "last-prefix": "آخرين",
+        "period": "{{ endTime }} تا {{ startTime }} از",
+        "edit": "ويرايش پنجره زماني",
+        "date-range": "بازه داده",
+        "last": "آخرين",
+        "time-period": "دوره زماني"
+    },
+    "user": {
+        "user": "کاربر",
+        "users": "کاربرها",
+        "customer-users": "کاربرهاي مشتري",
+        "tenant-admins": "مديران کاربر",
+        "sys-admin": "مدير سيستم",
+        "tenant-admin": "کاربر مدير",
+        "customer": "مشتري",
+        "anonymous": "بي نام",
+        "add": "افزودن کاربر",
+        "delete": "حذف کاربر",
+        "add-user-text": "افزودن کاربر جديد",
+        "no-users-text": "هيچ کاربري يافت نشد",
+        "user-details": "جزئيات کاربر",
+        "delete-user-title": "مطمئنيد؟ '{{userEmail}}' از حذف کاربر",
+        "delete-user-text": ".مراقب باشيد، پس از تأييد، کاربر و تمامي داده هاي مربوطه غير قابل بازيابي مي شوند",
+        "delete-users-title": "مطمئنيد؟ { count, plural, 1 {1 کاربر} other {# کاربر} } از حذف",
+        "delete-users-action-title": "{ count, plural, 1 {1 کاربر} other {کاربر #} } حذف",
+        "delete-users-text": ".مراقب باشيد، پس از تأييد، تمام کاربرهاي انتخاب شده حذف، و تمامي داده هاي مربوطه غير قابل بازيابي مي شوند",
+        "activation-email-sent-message": "!پست الکترونيک فعال سازي با موفقيت ارسال شد",
+        "resend-activation": "ارسال مجدد فعال سازي",
+        "email": "پست الکترونيک",
+        "email-required": ".پست الکترونيک مورد نياز است",
+        "invalid-email-format": ".قالب نامعتبر پست الکترونيک",
+        "first-name": "نام",
+        "last-name": "نام خانوادگي",
+        "description": "توصيف",
+        "default-dashboard": "داشبورد پيش فرض",
+        "always-fullscreen": "همواره در حالت تمام صفحه",
+        "select-user": "انتخاب کاربر",
+        "no-users-matching": ".يافت نشد '{{entity}}' هيچ کاربري منطبق بر",
+        "user-required": "کاربر مورد نياز است",
+        "activation-method": "روش فعال سازي",
+        "display-activation-link": "نمايش پيوند فعال سازي",
+        "send-activation-mail": "ارسال پست الکترونيک فعال سازي",
+        "activation-link": "پيوند فعال سازي کاربر",
+        "activation-link-text": ": </a>استفاده کنيد <a href='{{activationLink}}' target='_blank'> جهت فعال سازي کاربر، از پيوند فعال سازي زير",
+        "copy-activation-link": "رونوشت پيوند فعال سازي",
+        "activation-link-copied-message": "پيوند فعال سازي کاربر در حافظه موقت رونوشت شد",
+        "details": "جزئيات",
+        "login-as-tenant-admin": "ورود به عنوان کاربر مدير",
+        "login-as-customer-user": "ورود به عنوان کاربر مشتري"
+    },
+    "value": {
+        "type": "نوع مقدار",
+        "string": "رشته",
+        "string-value": "مقدار رشته",
+        "integer": "عدد صحيح",
+        "integer-value": "مقدار عدد صحيح",
+        "invalid-integer-value": "عدد صحيح نامعتبر",
+        "double": "دو برابر",
+        "double-value": "مقدار دو برابر",
+        "boolean": "بولين",
+        "boolean-value": "مقدار بولين",
+        "false": "نادرست",
+        "true": "صحيح",
+        "long": "بلند"
+    },
+    "widget": {
+        "widget-library": "کتابخانه ويجت ها",
+        "widget-bundle": "بسته ويجت",
+        "select-widgets-bundle": "انتخاب بسته ويجت",
+        "management": "مديريت ويجت",
+        "editor": "ويرايشگر ويجت",
+        "widget-type-not-found": ".نوع ويجت حذف شد \n احتمالا مرتبط است<br>.مشکل بارگذاري پيکربندي ويجت",
+        "widget-type-load-error": ":ويجت، به علت خطاهاي زير، بارگذاري نشد",
+        "remove": "حذف ويجت",
+        "edit": "ويرايش ويجت",
+        "remove-widget-title": "مطمئنيد؟ '{{widgetTitle}}' از حذف ويجت",
+        "remove-widget-text": ".پس از تأييد، ويجت و تمامي داده هاي مربوطه غير قابل بازيابي مي شوند",
+        "timeseries": "سري هاي زماني",
+        "search-data": "جستجوي داده",
+        "no-data-found": "هيچ داده اي يافت نشد",
+        "latest-values": "آخرين مقادير",
+        "rpc": "ويجت کنترل",
+        "alarm": "ويجت هشدار",
+        "static": "ويجت ايستا",
+        "select-widget-type": "انتخاب نوع ويجت",
+        "missing-widget-title-error": "!عنوان ويجت بايد مشخص شود",
+        "widget-saved": "ويجت ذخيره شد",
+        "unable-to-save-widget-error": "!ذخيره سازي ويجت ممکن نيست! ويجت خطاهايي دارد",
+        "save": "ذخيره سازي ويجت",
+        "saveAs": "ذخيره سازي ويجت به عنوان",
+        "save-widget-type-as": "ذخيره سازي نوع ويجت به عنوان",
+        "save-widget-type-as-text": "لطفا عنوان ويجت جديد را وارد کنيد و/يا بسته ويجت هدف را انتخاب نماييد",
+        "toggle-fullscreen": "حالت تمام صفحه را تغيير دهيد",
+        "run": "اجراي ويجت",
+        "title": "عنوان ويجت",
+        "title-required": ".عنوان ويجت مورد نياز است",
+        "type": "نوع ويجت",
+        "resources": "منابع",
+        "resource-url": "JavaScript/CSS URL",
+        "remove-resource": "حذف منبع",
+        "add-resource": "افزودن منبع",
+        "html": "HTML",
+        "tidy": "مرتب",
+        "css": "CSS",
+        "settings-schema": "طرح تنظيمات",
+        "datakey-settings-schema": "طرح تنظيمات کليد داده",
+        "javascript": "Javascript",
+        "remove-widget-type-title": "مطمئنيد؟ '{{widgetName}}' از حذف ويجت نوع",
+        "remove-widget-type-text": ".پس از تأييد، نوع ويجت و تمامي داده هاي مربوطه غير قابل بازيابي مي شوند",
+        "remove-widget-type": "حذف نوع ويجت",
+        "add-widget-type": "افزودن نوع ويجت جديد",
+        "widget-type-load-failed-error": "!عدم موفقيت در بارگذاري نوع ويجت",
+        "widget-template-load-failed-error": "!عدم موفقيت در بارگذاري قالب ويجت",
+        "add": "افزودن ويجت",
+        "undo": "برگرداندن تغييرات ويجت",
+        "export": "صدور ويجت"
+    },
+    "widget-action": {
+        "header-button": "دکمه هدر ويجت",
+        "open-dashboard-state": "هدايت به وضعيت داشبورد جديد",
+        "update-dashboard-state": "به روز رساني وضعيت داشبورد فعلي",
+        "open-dashboard": "هدايت به داشبورد ديگر",
+        "custom": "اقدام متداول",
+        "target-dashboard-state": "وضعيت داشبورد هدف",
+        "target-dashboard-state-required": "وضعيت داشبورد هدف مورد نياز است",
+        "set-entity-from-widget": "تنظيم موجودي از ويجت",
+        "target-dashboard": "داشبورد هدف",
+        "open-right-layout": "(طرح داشبورد سمت راست را باز کنيد (نماي تلفن همراه"
+    },
+    "widgets-bundle": {
+        "current": "بسته فعلي",
+        "widgets-bundles": "بسته هاي ويجت",
+        "add": "افزودن بسته ويجت",
+        "delete": "حذف بسته ويجت",
+        "title": "عنوان",
+        "title-required": ".عنوان مورد نياز است",
+        "add-widgets-bundle-text": "افزودن بسته ويجت جديد",
+        "no-widgets-bundles-text": "هيچ بسته ويجتي يافت نشد",
+        "empty": "بسته ويجت خالي است",
+        "details": "جزئيات",
+        "widgets-bundle-details": "جزئيات بسته ويجت",
+        "delete-widgets-bundle-title": "مطمئنيد؟ '{{widgetsBundleTitle}}' از حذف بسته ويجت",
+        "delete-widgets-bundle-text": ".مراقب باشيد، پس از تأييد، بسته ويجت و تمامي داده هاي مربوطه غير قابل بازيابي مي شوند",
+        "delete-widgets-bundles-title": "مطمئنيد؟ { count, plural, 1 {1 بسته ويجت} other {# بسته ويجت} } از حذف",
+        "delete-widgets-bundles-action-title": "{ count, plural, 1 {1 بسته ويجت} other {# بسته ويجت} } حذف",
+        "delete-widgets-bundles-text": ".مراقب باشيد، پس از تأييد، تمام بسته هاي ويجتِ انتخاب شده حذف، و تمامي داده هاي مربوطه غير قابل بازيابي مي شوند",
+        "no-widgets-bundles-matching": "يافت نشد '{{widgetsBundle}}' هيچ بسته ويجتي منطبق بر",
+        "widgets-bundle-required": ".بسته ويجت مورد نياز است",
+        "system": "سيستم",
+        "import": "وارد کردن بسته ويجت",
+        "export": "صدور بسته ويجت",
+        "export-failed-error": "{{error}} :صدور بسته ويجت ممکن نيست",
+        "create-new-widgets-bundle": "ايجاد بسته ويجت جديد",
+        "widgets-bundle-file": "پرونده بسته ويجت",
+        "invalid-widgets-bundle-file-error": ".وارد کردن بسته ويجت ممکن نيست: ساختار داده بسته ويجت نامعتبر است"
+    },
+    "widget-config": {
+        "data": "داده",
+        "settings": "تنظيمات",
+        "advanced": "پيشرفته",
+        "title": "عنوان",
+        "general-settings": "تنظيمات عمومي",
+        "display-title": "نمايش عنوان",
+        "drop-shadow": "سايه افتادن",
+        "enable-fullscreen": "فعال سازي حالت تمام صفحه",
+        "background-color": "رنگ پس زمينه",
+        "text-color": "رنگ متن",
+        "padding": "حاشيه داخلي",
+        "margin": "حاشيه",
+        "widget-style": "سبک ويجت",
+        "title-style": "سبک عنوان",
+        "mobile-mode-settings": "تنظيمات حالت تلفن همراه",
+        "order": "ترتيب",
+        "height": "ارتفاع",
+        "units": "کارکتر خاص براي نمايش بعد از مقدار تعين شده",
+        "decimals": "تعداد ارقام بعد از مميّز شناور",
+        "timewindow": "پنجره زمان",
+        "use-dashboard-timewindow": "استفاده از پنجره زمان داشبورد",
+        "display-legend": "نمايش فهرست علائم",
+        "datasources": "منابع داده",
+        "maximum-datasources": "{ count, plural, 1 {.1 منبع داده مجاز است} other {# منبع داده مجازند.} } بيشترين",
+        "datasource-type": "نوع",
+        "datasource-parameters": "پارامترها",
+        "remove-datasource": "حذف منبع داده",
+        "add-datasource": "افزودن منبع داده",
+        "target-device": "دستگاه هدف",
+        "alarm-source": "منشأ هشدار",
+        "actions": "اقدامات",
+        "action": "اقدام",
+        "add-action": "افزودن اقدام",
+        "search-actions": "جستجوي اقدامات",
+        "action-source": "منشأ اقدام",
+        "action-source-required": ".منشأ اقدام مورد نياز است",
+        "action-name": "نام",
+        "action-name-required": ".نام اقدام مورد نياز است",
+        "action-name-not-unique": ".در حيطه يک منشأ اقدام، نام اقدام بايد منحصر بفرد باشد<br/>.در حال حاضر اقدامي ديگر با نام مشابه موجود است",
+        "action-icon": "شمايل",
+        "action-type": "نوع",
+        "action-type-required": ".نوع اقدام مورد نياز است",
+        "edit-action": "ويرايش اقدام",
+        "delete-action": "حذف اقدام",
+        "delete-action-title": "حذف اقدام ويجت",
+        "delete-action-text": "مطمئنيد؟ '{{actionName}}' از حذف اقدام ويجت با نام"
+    },
+    "widget-type": {
+        "import": "وارد کردن نوع ويجت",
+        "export": "صدور نوع ويجت",
+        "export-failed-error": "{{error}} :صدور نوع ويجت ممکن نيست",
+        "create-new-widget-type": "ايجاد نوع جديد ويجت",
+        "widget-type-file": "پرونده نوع ويجت",
+        "invalid-widget-type-file-error": ".وارد کردن نوع ويجت ممکن نيست: ساختار داده نوع ويجت نامعتبر است"
+    },
+    "icon": {
+        "icon": "آيکون",
+        "select-icon": "انتخاب آيکون",
+        "material-icons": "آيکونهاي اجسام",
+        "show-all": "نمايش تمام آيکونها"
+    },
+    "custom": {
+        "widget-action": {
+            "action-cell-button": "دکمه سلول عملياتي",
+            "row-click": "در رديف کليک کنيد",
+            "marker-click": "روي نشانگر کليک کنيد",
+            "tooltip-tag-action": "اقدام برچسب راهنماي ابزار"
+        }
+    },
+    "language": {
+        "language": "زبان",
+        "locales": {
+            "de_DE": "آلمانی",
+			"fr_FR": "فرانسوي",
+            "zh_CN": "چيني",
+            "en_US": "انگليسي",
+            "it_IT": "ايتاليايي",
+            "ko_KR": "کره اي",
+            "ru_RU": "روسي",
+            "es_ES": "اسپانيولي",
+            "ja_JA": "ژاپني",
+            "tr_TR": "ترکي",
+			"fa_IR": "فارسي"
+        }
+    }
+}
diff --git a/ui/src/app/locale/locale.constant-fr_FR.json b/ui/src/app/locale/locale.constant-fr_FR.json
index 5b4edd9..651b695 100644
--- a/ui/src/app/locale/locale.constant-fr_FR.json
+++ b/ui/src/app/locale/locale.constant-fr_FR.json
@@ -1011,7 +1011,9 @@
             "ko_KR": "Coréen",
             "ru_RU": "Russe",
             "zh_CN": "Chinois",
-            "tr_TR": "Turc"
+            "ja_JA": "Japonaise",
+            "tr_TR": "Turc",
+            "fa_IR": "Persane"
         }
     },
     "layout": {
diff --git a/ui/src/app/locale/locale.constant-it_IT.json b/ui/src/app/locale/locale.constant-it_IT.json
index 4c73548..1d3a84f 100644
--- a/ui/src/app/locale/locale.constant-it_IT.json
+++ b/ui/src/app/locale/locale.constant-it_IT.json
@@ -1441,7 +1441,8 @@
             "ru_RU": "Russo",
             "es_ES": "Spagnolo",
             "ja_JA": "Giapponese",
-            "tr_TR": "Turco"
+            "tr_TR": "Turco",
+            "fa_IR": "Persiana"
         }
     }
 }
\ No newline at end of file
diff --git a/ui/src/app/locale/locale.constant-ja_JA.json b/ui/src/app/locale/locale.constant-ja_JA.json
index fea4ab3..ce4d849 100644
--- a/ui/src/app/locale/locale.constant-ja_JA.json
+++ b/ui/src/app/locale/locale.constant-ja_JA.json
@@ -1450,6 +1450,7 @@
 		"language": "言語",
 		"locales": {
 			"de_DE": "ドイツ語",
+            "fr_FR": "フランス語",
 			"en_US": "英語",
 			"ko_KR": "韓国語",
 			"it_IT": "イタリアの",
@@ -1457,7 +1458,8 @@
 			"ru_RU": "ロシア",
 			"es_ES": "スペイン語",
 			"ja_JA": "日本語",
-			"tr_TR": "トルコ語"
+			"tr_TR": "トルコ語",
+            "fa_IR": "ペルシャ語"
 		}
 	}
 }
\ No newline at end of file
diff --git a/ui/src/app/locale/locale.constant-ko_KR.json b/ui/src/app/locale/locale.constant-ko_KR.json
index 4ddd9b5..57aef0e 100644
--- a/ui/src/app/locale/locale.constant-ko_KR.json
+++ b/ui/src/app/locale/locale.constant-ko_KR.json
@@ -1334,7 +1334,8 @@
             "es_ES": "스페인어",
             "it_IT": "이탈리아 사람",
             "ja_JA": "일본어",
-            "tr_TR": "터키어"
+            "tr_TR": "터키어",
+            "fa_IR": "페르시아 인"
         }
     }
 }
\ No newline at end of file
diff --git a/ui/src/app/locale/locale.constant-ru_RU.json b/ui/src/app/locale/locale.constant-ru_RU.json
index c087649..d31d866 100644
--- a/ui/src/app/locale/locale.constant-ru_RU.json
+++ b/ui/src/app/locale/locale.constant-ru_RU.json
@@ -1573,7 +1573,8 @@
             "ru_RU": "Русский",
             "tr_TR": "Турецкий",
             "fr_FR": "Французский",
-            "ja_JA": "Японский"
+            "ja_JA": "Японский",
+            "fa_IR": "Персидский"
         }
     }
 }
\ No newline at end of file
diff --git a/ui/src/app/locale/locale.constant-tr_TR.json b/ui/src/app/locale/locale.constant-tr_TR.json
index 3f2e37e..b96eaaf 100644
--- a/ui/src/app/locale/locale.constant-tr_TR.json
+++ b/ui/src/app/locale/locale.constant-tr_TR.json
@@ -1540,7 +1540,8 @@
             "ru_RU": "Rusça",
             "es_ES": "İspanyol",
             "ja_JA": "Japonca",
-            "tr_TR": "Türkçe"
+            "tr_TR": "Türkçe",
+            "fa_IR": "Farsça"
         }
     }
 }
\ No newline at end of file
diff --git a/ui/src/app/locale/locale.constant-zh_CN.json b/ui/src/app/locale/locale.constant-zh_CN.json
index bacddc4..141c613 100644
--- a/ui/src/app/locale/locale.constant-zh_CN.json
+++ b/ui/src/app/locale/locale.constant-zh_CN.json
@@ -1444,7 +1444,8 @@
             "es_ES": "西班牙语",
             "it_IT": "意大利",
             "ja_JA": "日本",
-            "tr_TR": "土耳其"
+            "tr_TR": "土耳其",
+            "fa_IR": "波斯语"
         }
     }
 }
\ No newline at end of file
diff --git a/ui/webpack.config.dev.js b/ui/webpack.config.dev.js
index cfff811..dec2c77 100644
--- a/ui/webpack.config.dev.js
+++ b/ui/webpack.config.dev.js
@@ -18,13 +18,15 @@
 const HtmlWebpackPlugin = require('html-webpack-plugin');
 const ExtractTextPlugin = require('extract-text-webpack-plugin');
 const CopyWebpackPlugin = require('copy-webpack-plugin');
-const StyleLintPlugin = require('stylelint-webpack-plugin')
+const StyleLintPlugin = require('stylelint-webpack-plugin');
 
 const webpack = require('webpack');
 const path = require('path');
 const dirTree = require('directory-tree');
 const jsonminify = require("jsonminify");
 
+const HappyPack = require('happypack');
+
 const PUBLIC_RESOURCE_PATH = '/';
 
 var langs = [];
@@ -34,6 +36,9 @@ dirTree('./src/app/locale/', {extensions:/\.json$/}, (item) => {
     langs.push(item.name.slice(item.name.lastIndexOf('-') + 1, -5));
 });
 
+
+var happyThreadPool = HappyPack.ThreadPool({ size: 3 });
+
 /* devtool: 'cheap-module-eval-source-map', */
 
 module.exports = {
@@ -93,6 +98,21 @@ module.exports = {
             PUBLIC_PATH: JSON.stringify(PUBLIC_RESOURCE_PATH),
             SUPPORTED_LANGS: JSON.stringify(langs)
         }),
+        new HappyPack({
+            threadPool: happyThreadPool,
+            id: 'cached-babel',
+            loaders: ["babel-loader?cacheDirectory=true"]
+        }),
+        new HappyPack({
+            threadPool: happyThreadPool,
+            id: 'eslint',
+            loaders: ["eslint-loader?{parser: 'babel-eslint'}"]
+        }),
+        new HappyPack({
+            threadPool: happyThreadPool,
+            id: 'ng-annotate-and-cached-babel-loader',
+            loaders: ['ng-annotate', 'babel-loader?cacheDirectory=true']
+        })
     ],
     node: {
         tls: "empty",
@@ -102,19 +122,19 @@ module.exports = {
         loaders: [
             {
                 test: /\.jsx$/,
-                loader: 'babel',
+                loader: 'happypack/loader?id=cached-babel',
                 exclude: /node_modules/,
                 include: __dirname,
             },
             {
                 test: /\.js$/,
-                loaders: ['ng-annotate', 'babel'],
+                loaders: ['happypack/loader?id=ng-annotate-and-cached-babel-loader'],
                 exclude: /node_modules/,
                 include: __dirname,
             },
             {
                 test: /\.js$/,
-                loader: "eslint-loader?{parser: 'babel-eslint'}",
+                loader: 'happypack/loader?id=eslint',
                 exclude: /node_modules|vendor/,
                 include: __dirname,
             },
diff --git a/ui/webpack.config.prod.js b/ui/webpack.config.prod.js
index a442590..c0809fe 100644
--- a/ui/webpack.config.prod.js
+++ b/ui/webpack.config.prod.js
@@ -23,6 +23,8 @@ const webpack = require('webpack');
 const path = require('path');
 const dirTree = require('directory-tree');
 const jsonminify = require("jsonminify");
+const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
+const HappyPack = require('happypack');
 
 const PUBLIC_RESOURCE_PATH = '/static/';
 
@@ -33,6 +35,8 @@ dirTree('./src/app/locale/', {extensions:/\.json$/}, (item) => {
     langs.push(item.name.slice(item.name.lastIndexOf('-') + 1, -5));
 });
 
+var happyThreadPool = HappyPack.ThreadPool({ size: 3 });
+
 module.exports = {
     devtool: 'source-map',
     entry: [
@@ -95,6 +99,25 @@ module.exports = {
             test: /\.js$|\.css$|\.svg$|\.ttf$|\.woff$|\.woff2|\.eot$\.json$/,
             threshold: 10240,
             minRatio: 0.8
+        }),
+        new UglifyJsPlugin({
+            cache: true,
+            parallel: true
+        }),
+        new HappyPack({
+            threadPool: happyThreadPool,
+            id: 'cached-babel',
+            loaders: ["babel-loader?cacheDirectory=true"]
+        }),
+        new HappyPack({
+            threadPool: happyThreadPool,
+            id: 'eslint',
+            loaders: ["eslint-loader?{parser: 'babel-eslint'}"]
+        }),
+        new HappyPack({
+            threadPool: happyThreadPool,
+            id: 'ng-annotate-and-cached-babel-loader',
+            loaders: ['ng-annotate', 'babel-loader?cacheDirectory=true']
         })
     ],
     node: {
@@ -105,19 +128,20 @@ module.exports = {
         loaders: [
             {
                 test: /\.jsx$/,
-                loader: 'babel',
+                loader: 'happypack/loader?id=cached-babel',
                 exclude: /node_modules/,
                 include: __dirname,
             },
             {
                 test: /\.js$/,
-                loaders: ['ng-annotate', 'babel'],
+                loaders: ['happypack/loader?id=ng-annotate-and-cached-babel-loader'],
                 exclude: /node_modules/,
                 include: __dirname,
             },
             {
                 test: /\.js$/,
-                loader: "eslint-loader?{parser: 'babel-eslint'}",
+                loaders: ['happypack/loader?id=eslint'],
+                // loader: "eslint-loader?{parser: 'babel-eslint'}",
                 exclude: /node_modules|vendor/,
                 include: __dirname,
             },