From 87a97cb46211f3a5ce2c180f95a78cbd32a40c9b Mon Sep 17 00:00:00 2001 From: descipher Date: Mon, 18 Mar 2019 20:28:45 -0500 Subject: [PATCH 01/12] Add full Canadian postal code validation. --- ui/src/app/components/contact.directive.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/app/components/contact.directive.js b/ui/src/app/components/contact.directive.js index 93912882ed..cab6f52111 100644 --- a/ui/src/app/components/contact.directive.js +++ b/ui/src/app/components/contact.directive.js @@ -283,7 +283,7 @@ function Contact($compile, $templateCache) { "Austria": "[0-9]{4}", "Belgium": "[0-9]{4}", "Brazil": "[0-9]{5}[\\-]?[0-9]{3}", - "Canada": "[A-Za-z][0-9][A-Za-z] [0-9][A-Za-z][0-9]", + "Canada": "^(?!.*[DFIOQU])[A-VXY][0-9][A-Z][ -]?[0-9][A-Z][0-9]$", "Denmark": "[0-9]{3,4}", "Faroe Islands": "[0-9]{3,4}", "Netherlands": "[1-9][0-9]{3}\\s?[a-zA-Z]{2}", From 0285832bed0c83a89cb34917215dcc38ef63ebf4 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Thu, 21 Mar 2019 15:07:14 +0200 Subject: [PATCH 02/12] Fixed CORS mapping --- application/src/main/resources/thingsboard.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 72b8159407..f52bfccabc 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -273,7 +273,7 @@ updates: spring.mvc.cors: mappings: # Intercept path - "/api/auth/**": + "[/api/auth/**]": #Comma-separated list of origins to allow. '*' allows all origins. When not set,CORS support is disabled. allowed-origins: "*" #Comma-separated list of methods to allow. '*' allows all methods. @@ -284,7 +284,7 @@ spring.mvc.cors: max-age: "1800" #Set whether credentials are supported. When not set, credentials are not supported. allow-credentials: "true" - "/api/v1/**": + "[/api/v1/**]": allowed-origins: "*" allowed-methods: "*" allowed-headers: "*" From 1a1e66fa56a7da37da17aa187cacc1f8a1a4c3d3 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Thu, 21 Mar 2019 17:38:27 +0200 Subject: [PATCH 03/12] Fixed CORS mapping --- application/src/main/resources/thingsboard.yml | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index f52bfccabc..1069f9d6e6 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -273,23 +273,17 @@ updates: spring.mvc.cors: mappings: # Intercept path - "[/api/auth/**]": + "[/api/**]": #Comma-separated list of origins to allow. '*' allows all origins. When not set,CORS support is disabled. allowed-origins: "*" #Comma-separated list of methods to allow. '*' allows all methods. - allowed-methods: "POST,GET,OPTIONS" + allowed-methods: "*" #Comma-separated list of headers to allow in a request. '*' allows all headers. allowed-headers: "*" #How long, in seconds, the response from a pre-flight request can be cached by clients. max-age: "1800" #Set whether credentials are supported. When not set, credentials are not supported. allow-credentials: "true" - "[/api/v1/**]": - allowed-origins: "*" - allowed-methods: "*" - allowed-headers: "*" - max-age: "1800" - allow-credentials: "true" # spring serve gzip compressed static resources spring.resources.chain: From 13e1d60a7cb558040238958e4808a2262e21d644 Mon Sep 17 00:00:00 2001 From: Sergey Tarnavskiy Date: Tue, 26 Mar 2019 12:29:30 +0200 Subject: [PATCH 04/12] added feature for resetting debug-mode in all rule-nodes --- ui/src/app/locale/locale.constant-en_US.json | 3 ++- ui/src/app/locale/locale.constant-ru_RU.json | 3 ++- ui/src/app/rulechain/rulechain.controller.js | 16 ++++++++++++++++ ui/src/app/rulechain/rulechain.tpl.html | 9 +++++++++ 4 files changed, 29 insertions(+), 2 deletions(-) diff --git a/ui/src/app/locale/locale.constant-en_US.json b/ui/src/app/locale/locale.constant-en_US.json index dfef502f09..d1fe2c88c9 100644 --- a/ui/src/app/locale/locale.constant-en_US.json +++ b/ui/src/app/locale/locale.constant-en_US.json @@ -1295,7 +1295,8 @@ "metadata-required": "Metadata entries can't be empty.", "output": "Output", "test": "Test", - "help": "Help" + "help": "Help", + "reset-debug-mode": "Reset debug mode in all nodes" }, "tenant": { "tenant": "Tenant", diff --git a/ui/src/app/locale/locale.constant-ru_RU.json b/ui/src/app/locale/locale.constant-ru_RU.json index 3ad4371cc1..2d05051de8 100644 --- a/ui/src/app/locale/locale.constant-ru_RU.json +++ b/ui/src/app/locale/locale.constant-ru_RU.json @@ -1288,7 +1288,8 @@ "metadata-required": "Метаданные объекта не могут быть пустыми.", "output": "Выход", "test": "Протестировать", - "help": "Помощь" + "help": "Помощь", + "reset-debug-mode": "Сбросить режим отладки во всех правилах" }, "tenant": { "tenant": "Владелец", diff --git a/ui/src/app/rulechain/rulechain.controller.js b/ui/src/app/rulechain/rulechain.controller.js index 3744b63198..195dcffef5 100644 --- a/ui/src/app/rulechain/rulechain.controller.js +++ b/ui/src/app/rulechain/rulechain.controller.js @@ -108,6 +108,9 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time vm.objectsSelected = objectsSelected; vm.deleteSelected = deleteSelected; + vm.isDebugModeEnabled = isDebugModeEnabled; + vm.resetDebugModeInAllNodes = resetDebugModeInAllNodes; + vm.triggerResize = triggerResize; vm.openRuleChainContextMenu = openRuleChainContextMenu; @@ -1342,6 +1345,19 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time vm.modelservice.deleteSelected(); } + function isDebugModeEnabled() { + var res = $filter('filter')(vm.ruleChainModel.nodes, {debugMode: true}); + return (res && res.length); + } + + function resetDebugModeInAllNodes() { + vm.ruleChainModel.nodes.forEach((node) => { + if (node.component.type != types.ruleNodeType.INPUT.value && node.component.type != types.ruleNodeType.RULE_CHAIN.value) { + node.debugMode = false; + } + }); + } + function triggerResize() { var w = angular.element($window); w.triggerHandler('resize'); diff --git a/ui/src/app/rulechain/rulechain.tpl.html b/ui/src/app/rulechain/rulechain.tpl.html index 80425e59c8..4a20947497 100644 --- a/ui/src/app/rulechain/rulechain.tpl.html +++ b/ui/src/app/rulechain/rulechain.tpl.html @@ -223,6 +223,15 @@ + + + {{ 'rulenode.reset-debug-mode' | translate }} + + + Date: Wed, 27 Mar 2019 13:38:06 +0200 Subject: [PATCH 05/12] Improve zookeeper client reconnect logic. UI: Flot widget - Fixed incorrect individual tooltip content. --- .../cluster/discovery/ZkDiscoveryService.java | 37 +++++++++++++++---- pom.xml | 4 +- ui/src/app/widget/lib/flot-widget.js | 10 +++-- 3 files changed, 37 insertions(+), 14 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/cluster/discovery/ZkDiscoveryService.java b/application/src/main/java/org/thingsboard/server/service/cluster/discovery/ZkDiscoveryService.java index 5d1262f233..e44c28dc39 100644 --- a/application/src/main/java/org/thingsboard/server/service/cluster/discovery/ZkDiscoveryService.java +++ b/application/src/main/java/org/thingsboard/server/service/cluster/discovery/ZkDiscoveryService.java @@ -52,6 +52,8 @@ import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import java.util.List; import java.util.NoSuchElementException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.stream.Collectors; import static org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent.Type.CHILD_REMOVED; @@ -96,11 +98,13 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi @Lazy private ClusterRoutingService routingService; + private ExecutorService reconnectExecutorService; + private CuratorFramework client; private PathChildrenCache cache; private String nodePath; - private volatile boolean stopped = false; + private volatile boolean stopped = true; @PostConstruct public void init() { @@ -110,9 +114,15 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi Assert.notNull(zkConnectionTimeout, MiscUtils.missingProperty("zk.connection_timeout_ms")); Assert.notNull(zkSessionTimeout, MiscUtils.missingProperty("zk.session_timeout_ms")); + reconnectExecutorService = Executors.newSingleThreadExecutor(); + log.info("Initializing discovery service using ZK connect string: {}", zkUrl); zkNodesDir = zkDir + "/nodes"; + initZkClient(); + } + + private void initZkClient() { try { client = CuratorFrameworkFactory.newClient(zkUrl, zkSessionTimeout, zkConnectionTimeout, new RetryForever(zkRetryInterval)); client.start(); @@ -120,6 +130,8 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi cache = new PathChildrenCache(client, zkNodesDir, true); cache.getListenable().addListener(this); cache.start(); + stopped = false; + log.info("ZK client connected"); } catch (Exception e) { log.error("Failed to connect to ZK: {}", e.getMessage(), e); CloseableUtils.closeQuietly(cache); @@ -128,12 +140,20 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi } } - @PreDestroy - public void destroy() { + private void destroyZkClient() { stopped = true; - unpublishCurrentServer(); + try { + unpublishCurrentServer(); + } catch (Exception e) {} CloseableUtils.closeQuietly(cache); CloseableUtils.closeQuietly(client); + log.info("ZK client disconnected"); + } + + @PreDestroy + public void destroy() { + destroyZkClient(); + reconnectExecutorService.shutdownNow(); log.info("Stopped discovery service"); } @@ -180,20 +200,21 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi return (client, newState) -> { log.info("[{}:{}] ZK state changed: {}", self.getHost(), self.getPort(), newState); if (newState == ConnectionState.LOST) { - reconnect(); + reconnectExecutorService.submit(this::reconnect); } }; } - private boolean reconnectInProgress = false; + private volatile boolean reconnectInProgress = false; private synchronized void reconnect() { if (!reconnectInProgress) { reconnectInProgress = true; try { - client.blockUntilConnected(); + destroyZkClient(); + initZkClient(); publishCurrentServer(); - } catch (InterruptedException e) { + } catch (Exception e) { log.error("Failed to reconnect to ZK: {}", e.getMessage(), e); } finally { reconnectInProgress = false; diff --git a/pom.xml b/pom.xml index b0bf2a51c3..59cc98aef0 100755 --- a/pom.xml +++ b/pom.xml @@ -50,7 +50,7 @@ 1.5.0 2.5 1.4 - 2.9.7 + 2.9.8 2.2.6 2.11 2.4.2 @@ -59,7 +59,7 @@ 1.7 2.0 1.4.3 - 4.0.1 + 4.2.0 3.6.1 1.16.1 1.16.18 diff --git a/ui/src/app/widget/lib/flot-widget.js b/ui/src/app/widget/lib/flot-widget.js index 8c9aca62c6..512f25b4ce 100644 --- a/ui/src/app/widget/lib/flot-widget.js +++ b/ui/src/app/widget/lib/flot-widget.js @@ -137,9 +137,11 @@ export default class TbFlot { }); content += dateDiv.prop('outerHTML'); if (tbFlot.ctx.tooltipIndividual) { - var seriesHoverInfo = hoverInfo.seriesHover[seriesIndex]; - if (seriesHoverInfo) { - content += seriesInfoDivFromInfo(seriesHoverInfo, seriesIndex); + var found = hoverInfo.seriesHover.filter((seriesHover) => { + return seriesHover.index === seriesIndex; + }); + if (found && found.length) { + content += seriesInfoDivFromInfo(found[0], seriesIndex); } } else { var seriesDiv = $('
'); @@ -161,7 +163,7 @@ export default class TbFlot { if (i == hoverInfo.seriesHover.length) { break; } - seriesHoverInfo = hoverInfo.seriesHover[i]; + var seriesHoverInfo = hoverInfo.seriesHover[i]; columnContent += seriesInfoDivFromInfo(seriesHoverInfo, seriesIndex); } columnDiv.html(columnContent); From 080b0663ca377253b1a78af988c3e1bf2c371ee5 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 27 Mar 2019 14:50:23 +0200 Subject: [PATCH 06/12] UI: Add searchbox support for all ace editors. --- ui/src/app/alarm/alarm-details-dialog.controller.js | 1 + ui/src/app/audit/audit-log-details-dialog.controller.js | 1 + ui/src/app/components/js-func.directive.js | 1 + ui/src/app/components/json-content.directive.js | 1 + ui/src/app/components/json-object-edit.directive.js | 1 + ui/src/app/components/react/json-form-ace-editor.jsx | 1 + ui/src/app/entity/relation/relation-dialog.controller.js | 1 + ui/src/app/event/event-content-dialog.controller.js | 1 + .../extension/extensions-forms/extension-form-http.directive.js | 1 + .../extensions-forms/extension-form-modbus.directive.js | 1 + .../extension/extensions-forms/extension-form-opc.directive.js | 1 + ui/src/app/widget/widget-editor.controller.js | 1 + 12 files changed, 12 insertions(+) diff --git a/ui/src/app/alarm/alarm-details-dialog.controller.js b/ui/src/app/alarm/alarm-details-dialog.controller.js index f5e6c92e1c..3da3fa616e 100644 --- a/ui/src/app/alarm/alarm-details-dialog.controller.js +++ b/ui/src/app/alarm/alarm-details-dialog.controller.js @@ -14,6 +14,7 @@ * limitations under the License. */ import 'brace/ext/language_tools'; +import 'brace/ext/searchbox'; import 'brace/mode/json'; import 'brace/theme/github'; import beautify from 'js-beautify'; diff --git a/ui/src/app/audit/audit-log-details-dialog.controller.js b/ui/src/app/audit/audit-log-details-dialog.controller.js index ec839d5d3d..1093abc05a 100644 --- a/ui/src/app/audit/audit-log-details-dialog.controller.js +++ b/ui/src/app/audit/audit-log-details-dialog.controller.js @@ -15,6 +15,7 @@ */ import $ from 'jquery'; import 'brace/ext/language_tools'; +import 'brace/ext/searchbox'; import 'brace/mode/java'; import 'brace/theme/github'; diff --git a/ui/src/app/components/js-func.directive.js b/ui/src/app/components/js-func.directive.js index cb3d58788f..bb600b789e 100644 --- a/ui/src/app/components/js-func.directive.js +++ b/ui/src/app/components/js-func.directive.js @@ -17,6 +17,7 @@ import './js-func.scss'; import ace from 'brace'; import 'brace/ext/language_tools'; +import 'brace/ext/searchbox'; import $ from 'jquery'; import thingsboardToast from '../services/toast'; import thingsboardUtils from '../common/utils.service'; diff --git a/ui/src/app/components/json-content.directive.js b/ui/src/app/components/json-content.directive.js index 6686059a95..668d163a6d 100644 --- a/ui/src/app/components/json-content.directive.js +++ b/ui/src/app/components/json-content.directive.js @@ -16,6 +16,7 @@ import './json-content.scss'; import 'brace/ext/language_tools'; +import 'brace/ext/searchbox'; import 'brace/mode/json'; import 'brace/mode/text'; import 'brace/snippets/json'; diff --git a/ui/src/app/components/json-object-edit.directive.js b/ui/src/app/components/json-object-edit.directive.js index cc4ada19b6..a79eecb64e 100644 --- a/ui/src/app/components/json-object-edit.directive.js +++ b/ui/src/app/components/json-object-edit.directive.js @@ -16,6 +16,7 @@ import './json-object-edit.scss'; import 'brace/ext/language_tools'; +import 'brace/ext/searchbox'; import 'brace/mode/json'; import 'brace/snippets/json'; diff --git a/ui/src/app/components/react/json-form-ace-editor.jsx b/ui/src/app/components/react/json-form-ace-editor.jsx index 44df6cd38d..0720b86a41 100644 --- a/ui/src/app/components/react/json-form-ace-editor.jsx +++ b/ui/src/app/components/react/json-form-ace-editor.jsx @@ -21,6 +21,7 @@ import reactCSS from 'reactcss'; import AceEditor from 'react-ace'; import FlatButton from 'material-ui/FlatButton'; import 'brace/ext/language_tools'; +import 'brace/ext/searchbox'; import 'brace/theme/github'; import fixAceEditor from './../ace-editor-fix'; diff --git a/ui/src/app/entity/relation/relation-dialog.controller.js b/ui/src/app/entity/relation/relation-dialog.controller.js index 7ce2d4bd51..cd4bcebcfb 100644 --- a/ui/src/app/entity/relation/relation-dialog.controller.js +++ b/ui/src/app/entity/relation/relation-dialog.controller.js @@ -14,6 +14,7 @@ * limitations under the License. */ import 'brace/ext/language_tools'; +import 'brace/ext/searchbox'; import 'brace/mode/json'; import 'brace/theme/github'; import beautify from 'js-beautify'; diff --git a/ui/src/app/event/event-content-dialog.controller.js b/ui/src/app/event/event-content-dialog.controller.js index 80fc739056..9fb8a5a4ec 100644 --- a/ui/src/app/event/event-content-dialog.controller.js +++ b/ui/src/app/event/event-content-dialog.controller.js @@ -15,6 +15,7 @@ */ import $ from 'jquery'; import 'brace/ext/language_tools'; +import 'brace/ext/searchbox'; import 'brace/mode/java'; import 'brace/theme/github'; import beautify from 'js-beautify'; diff --git a/ui/src/app/extension/extensions-forms/extension-form-http.directive.js b/ui/src/app/extension/extensions-forms/extension-form-http.directive.js index 47a2b860c5..616d70d0e4 100644 --- a/ui/src/app/extension/extensions-forms/extension-form-http.directive.js +++ b/ui/src/app/extension/extensions-forms/extension-form-http.directive.js @@ -14,6 +14,7 @@ * limitations under the License. */ import 'brace/ext/language_tools'; +import 'brace/ext/searchbox'; import 'brace/mode/json'; import 'brace/theme/github'; diff --git a/ui/src/app/extension/extensions-forms/extension-form-modbus.directive.js b/ui/src/app/extension/extensions-forms/extension-form-modbus.directive.js index 36686be529..52b2e16a1d 100644 --- a/ui/src/app/extension/extensions-forms/extension-form-modbus.directive.js +++ b/ui/src/app/extension/extensions-forms/extension-form-modbus.directive.js @@ -14,6 +14,7 @@ * limitations under the License. */ import 'brace/ext/language_tools'; +import 'brace/ext/searchbox'; import 'brace/mode/json'; import 'brace/theme/github'; diff --git a/ui/src/app/extension/extensions-forms/extension-form-opc.directive.js b/ui/src/app/extension/extensions-forms/extension-form-opc.directive.js index a9b1d73519..1598fd04ef 100644 --- a/ui/src/app/extension/extensions-forms/extension-form-opc.directive.js +++ b/ui/src/app/extension/extensions-forms/extension-form-opc.directive.js @@ -14,6 +14,7 @@ * limitations under the License. */ import 'brace/ext/language_tools'; +import 'brace/ext/searchbox'; import 'brace/mode/json'; import 'brace/theme/github'; diff --git a/ui/src/app/widget/widget-editor.controller.js b/ui/src/app/widget/widget-editor.controller.js index b9d861a647..6beb0a6213 100644 --- a/ui/src/app/widget/widget-editor.controller.js +++ b/ui/src/app/widget/widget-editor.controller.js @@ -16,6 +16,7 @@ import $ from 'jquery'; import ace from 'brace'; import 'brace/ext/language_tools'; +import 'brace/ext/searchbox'; import 'brace/mode/javascript'; import 'brace/mode/html'; import 'brace/mode/css'; From 0d29b130ba766c8e0d85ad68b053c6257d29acea Mon Sep 17 00:00:00 2001 From: Bartosz Ptaszynski Date: Wed, 27 Mar 2019 13:22:19 +0000 Subject: [PATCH 07/12] Fix deployment on GKE When creating postgres deployment on GKE Postgres complains about data folder being mounted directly on root of the PV claim. ``` The files belonging to this database system will be owned by user "postgres". This user must also own the server process. The database cluster will be initialized with locale "en_US.utf8". The default database encoding has accordingly been set to "UTF8". The default text search configuration will be set to "english". Data page checksums are disabled. initdb: directory "/var/lib/postgresql/data" exists but is not empty It contains a lost+found directory, perhaps due to it being a mount point. Using a mount point directly as the data directory is not recommended. Create a subdirectory under the mount point. ``` A simple solution is to force Postgres to use a sub folder using PGDATA env variable. --- k8s/postgres.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/k8s/postgres.yml b/k8s/postgres.yml index 2e7f2cc815..404b564933 100644 --- a/k8s/postgres.yml +++ b/k8s/postgres.yml @@ -55,6 +55,8 @@ spec: env: - name: POSTGRES_DB value: "thingsboard" + - name: PGDATA + value: /var/lib/postgresql/data/pgdata volumeMounts: - mountPath: /var/lib/postgresql/data name: postgres-data From bb68e575e7e0ba95dccbfea3562fcd2d9c4481c3 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 28 Mar 2019 09:51:43 +0200 Subject: [PATCH 08/12] UI: Settings cards layout improvement. --- ui/src/app/admin/admin.controller.js | 3 +++ ui/src/app/admin/general-settings.tpl.html | 4 ++-- .../app/admin/outgoing-mail-settings.tpl.html | 4 ++-- ui/src/app/admin/settings-card.scss | 23 +++++++++++++++++++ 4 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 ui/src/app/admin/settings-card.scss diff --git a/ui/src/app/admin/admin.controller.js b/ui/src/app/admin/admin.controller.js index c697726137..1fd0b22eeb 100644 --- a/ui/src/app/admin/admin.controller.js +++ b/ui/src/app/admin/admin.controller.js @@ -13,6 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +import './settings-card.scss'; + /*@ngInject*/ export default function AdminController(adminService, toast, $scope, $rootScope, $state, $translate) { diff --git a/ui/src/app/admin/general-settings.tpl.html b/ui/src/app/admin/general-settings.tpl.html index 7023e133dd..0e651f8233 100644 --- a/ui/src/app/admin/general-settings.tpl.html +++ b/ui/src/app/admin/general-settings.tpl.html @@ -15,8 +15,8 @@ limitations under the License. --> -
- +
+ admin.general-settings diff --git a/ui/src/app/admin/outgoing-mail-settings.tpl.html b/ui/src/app/admin/outgoing-mail-settings.tpl.html index 14049defd3..855da256c6 100644 --- a/ui/src/app/admin/outgoing-mail-settings.tpl.html +++ b/ui/src/app/admin/outgoing-mail-settings.tpl.html @@ -15,8 +15,8 @@ limitations under the License. --> -
- +
+ admin.outgoing-mail-settings diff --git a/ui/src/app/admin/settings-card.scss b/ui/src/app/admin/settings-card.scss new file mode 100644 index 0000000000..9cbf5266b0 --- /dev/null +++ b/ui/src/app/admin/settings-card.scss @@ -0,0 +1,23 @@ +/** + * Copyright © 2016-2019 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@import "../../scss/constants"; + +md-card.settings-card { + @media (min-width: $layout-breakpoint-sm) { + width: 60%; + } +} From 2f06be1d0557a583617dc86177287ccd7487dd5c Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 28 Mar 2019 11:27:01 +0200 Subject: [PATCH 09/12] UI: Widgets - add entityDescription variable --- ui/src/app/common/utils.service.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui/src/app/common/utils.service.js b/ui/src/app/common/utils.service.js index 0b8d23a726..f140893d2b 100644 --- a/ui/src/app/common/utils.service.js +++ b/ui/src/app/common/utils.service.js @@ -499,6 +499,8 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, t label = label.split(variable).join(datasource.entityName); } else if (variableName === 'aliasName') { label = label.split(variable).join(datasource.aliasName); + } else if (variableName === 'entityDescription') { + label = label.split(variable).join(datasource.entityDescription); } match = varsRegex.exec(pattern); } From 2bc13b68122912fad60d78744cb110c1c2e9b713 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 28 Mar 2019 18:36:43 +0200 Subject: [PATCH 10/12] UI: Trip Animation Widget Improvements. --- .../data/json/system/widget_bundles/maps.json | 2 +- .../tripAnimation/trip-animation-widget.js | 151 +++++++++++----- .../tripAnimation/trip-animation-widget.scss | 162 +++++++++++------- .../trip-animation-widget.tpl.html | 59 ++++--- 4 files changed, 238 insertions(+), 136 deletions(-) diff --git a/application/src/main/data/json/system/widget_bundles/maps.json b/application/src/main/data/json/system/widget_bundles/maps.json index 1f4807c7d4..66c87e1536 100644 --- a/application/src/main/data/json/system/widget_bundles/maps.json +++ b/application/src/main/data/json/system/widget_bundles/maps.json @@ -130,7 +130,7 @@ "controllerScript": " self.onInit = function() {\n var $scope = self.ctx.$scope;\n $scope.self = self;\n }\n \n \n self.actionSources = function () {\n return {\n 'tooltipAction': {\n name: 'widget-action.tooltip-tag-action',\n multiple: false\n }\n }\n };\n", "settingsSchema": "{\r\n \"schema\": {\r\n \"title\": \"Openstreet Map Configuration\",\r\n \"type\": \"object\",\r\n \"properties\": {\r\n \"mapProvider\": {\r\n \"title\": \"Map provider\",\r\n \"type\": \"string\",\r\n \"default\": \"OpenStreetMap.Mapnik\"\r\n },\r\n \"defaultZoomLevel\": {\r\n\t\t\t\t\t\"title\": \"Default map zoom level (1 - 20)\",\r\n\t\t\t\t\t\"type\": \"number\"\r\n\t\t\t\t},\r\n\t\t\t\"fitMapBounds\": {\r\n\t\t\t\t\"title\": \"Fit map bounds to cover all markers\",\r\n\t\t\t\t\"type\": \"boolean\",\r\n\t\t\t\t\"default\": true\r\n\t\t\t},\r\n \"latKeyName\": {\r\n \"title\": \"Latitude key name\",\r\n \"type\": \"string\",\r\n \"default\": \"latitude\"\r\n },\r\n \"lngKeyName\": {\r\n \"title\": \"Longitude key name\",\r\n \"type\": \"string\",\r\n \"default\": \"longitude\"\r\n },\r\n \"showLabel\": {\r\n \"title\": \"Show label\",\r\n \"type\": \"boolean\",\r\n \"default\": true\r\n },\r\n \"label\": {\r\n \"title\": \"Label (pattern examples: '${entityName}', '${entityName}: (Text ${keyName} units.)' )\",\r\n \"type\": \"string\",\r\n \"default\": \"${entityName}\"\r\n },\r\n \"useLabelFunction\": {\r\n \"title\": \"Use label function\",\r\n \"type\": \"boolean\",\r\n \"default\": false\r\n },\r\n \"labelFunction\": {\r\n \"title\": \"Label function: f(data, dsData, dsIndex)\",\r\n \"type\": \"string\"\r\n },\r\n \"showTooltip\": {\r\n \"title\": \"Show tooltip\",\r\n \"type\": \"boolean\",\r\n \"default\": true\r\n },\r\n \"tooltipColor\": {\r\n \"title\": \"Tooltip background color\",\r\n \"type\": \"string\",\r\n \"default\": \"#fff\"\r\n },\r\n \"tooltipFontColor\": {\r\n \"title\": \"Tooltip font color\",\r\n \"type\": \"string\",\r\n \"default\": \"#000\"\r\n },\r\n \"tooltipOpacity\": {\r\n \"title\": \"Tooltip opacity (0-1)\",\r\n \"type\": \"number\",\r\n \"default\": 1 \r\n },\r\n \"tooltipPattern\": {\r\n \"title\": \"Tooltip (for ex. 'Text ${keyName} units.' or Link text')\",\r\n \"type\": \"string\",\r\n \"default\": \"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
End Time: ${maxTime}
Start Time: ${minTime}\"\r\n },\r\n \"useTooltipFunction\": {\r\n \"title\": \"Use tooltip function\",\r\n \"type\": \"boolean\",\r\n \"default\": false\r\n },\r\n \"tooltipFunction\": {\r\n \"title\": \"Tooltip function: f(data, dsData, dsIndex)\",\r\n \"type\": \"string\"\r\n },\r\n \"color\": {\r\n \"title\": \"Stroke color\",\r\n \"type\": \"string\"\r\n },\r\n \"strokeWeight\": {\r\n \"title\": \"Stroke weight\",\r\n \"type\": \"number\",\r\n \"default\": 2\r\n },\r\n \"strokeOpacity\": {\r\n \"title\": \"Stroke opacity\",\r\n \"type\": \"number\",\r\n \"default\": 1\r\n },\r\n \"useColorFunction\": {\r\n \"title\": \"Use stroke color function\",\r\n \"type\": \"boolean\",\r\n \"default\": false\r\n },\r\n \"colorFunction\": {\r\n \"title\": \"Stroke color function: f(data, dsData, dsIndex)\",\r\n \"type\": \"string\"\r\n },\r\n \"showPoints\": {\r\n \"title\": \"Show points\",\r\n \"type\": \"boolean\",\r\n \"default\": false\r\n },\r\n \"pointColor\": {\r\n \"title\": \"Point color\",\r\n \"type\": \"string\"\r\n },\r\n \"pointSize\": {\r\n \"title\": \"Point size (px)\",\r\n \"type\": \"number\",\r\n \"default\": 10\r\n },\r\n \"defaultMarkerColor\": {\r\n \"title\": \"color for default marker\",\r\n \"type\": \"string\"\r\n },\r\n \"markerImage\": {\r\n \"title\": \"Custom marker image\",\r\n \"type\": \"string\"\r\n },\r\n \"markerImageSize\": {\r\n \"title\": \"Custom marker image size (px)\",\r\n \"type\": \"number\",\r\n \"default\": 34\r\n },\r\n \"rotationAngle\": {\r\n \"title\": \"Set additional rotation angle for marker (deg)\",\r\n \"type\": \"number\",\r\n \"default\": 180\r\n },\r\n \"useMarkerImageFunction\":{\r\n \"title\":\"Use marker image function\",\r\n \"type\":\"boolean\",\r\n \"default\":false\r\n },\r\n \"markerImageFunction\":{\r\n \"title\":\"Marker image function: f(data, images, dsData, dsIndex)\",\r\n \"type\":\"string\"\r\n },\r\n \"markerImages\":{\r\n \"title\":\"Marker images\",\r\n \"type\":\"array\",\r\n \"items\":{\r\n \"title\":\"Marker image\",\r\n \"type\":\"string\"\r\n }\r\n }\r\n },\r\n \"required\": []\r\n },\r\n \"form\": [{\r\n \"key\": \"mapProvider\",\r\n \"type\": \"rc-select\",\r\n \"multiple\": false,\r\n \"items\": [{\r\n \"value\": \"OpenStreetMap.Mapnik\",\r\n \"label\": \"OpenStreetMap.Mapnik (Default)\"\r\n }, {\r\n \"value\": \"OpenStreetMap.BlackAndWhite\",\r\n \"label\": \"OpenStreetMap.BlackAndWhite\"\r\n }, {\r\n \"value\": \"OpenStreetMap.HOT\",\r\n \"label\": \"OpenStreetMap.HOT\"\r\n }, {\r\n \"value\": \"Esri.WorldStreetMap\",\r\n \"label\": \"Esri.WorldStreetMap\"\r\n }, {\r\n \"value\": \"Esri.WorldTopoMap\",\r\n \"label\": \"Esri.WorldTopoMap\"\r\n }, {\r\n \"value\": \"CartoDB.Positron\",\r\n \"label\": \"CartoDB.Positron\"\r\n }, {\r\n \"value\": \"CartoDB.DarkMatter\",\r\n \"label\": \"CartoDB.DarkMatter\"\r\n }]\r\n },\"defaultZoomLevel\", \"fitMapBounds\", \"latKeyName\", \"lngKeyName\", \"showLabel\", \"label\", \"useLabelFunction\", {\r\n \"key\": \"labelFunction\",\r\n \"type\": \"javascript\"\r\n }, \"showTooltip\", {\r\n \"key\": \"tooltipColor\",\r\n \"type\": \"color\"\r\n }, {\r\n \"key\": \"tooltipFontColor\",\r\n \"type\": \"color\"\r\n },\"tooltipOpacity\", {\r\n \"key\": \"tooltipPattern\",\r\n \"type\": \"textarea\"\r\n }, \"useTooltipFunction\", {\r\n \"key\": \"tooltipFunction\",\r\n \"type\": \"javascript\"\r\n }, {\r\n \"key\": \"color\",\r\n \"type\": \"color\"\r\n }, \"useColorFunction\", {\r\n \"key\": \"colorFunction\",\r\n \"type\": \"javascript\"\r\n }, \"strokeWeight\", \"strokeOpacity\", \"showPoints\",{\r\n \"key\": \"pointColor\",\r\n \"type\": \"color\"\r\n }, \"pointSize\", {\r\n \"key\": \"defaultMarkerColor\",\r\n \"type\": \"color\"\r\n }, {\r\n \"key\": \"markerImage\",\r\n \"type\": \"image\"\r\n }, \"markerImageSize\", \"rotationAngle\",\"useMarkerImageFunction\",\r\n {\r\n \"key\":\"markerImageFunction\",\r\n \"type\":\"javascript\"\r\n }, {\r\n \"key\":\"markerImages\",\r\n \"items\":[\r\n {\r\n \"key\":\"markerImages[]\",\r\n \"type\":\"image\"\r\n }\r\n ]\r\n }]\r\n}", "dataKeySettingsSchema": "{}", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var lats = [37.7696499,\\n37.7699074,\\n37.7699536,\\n37.7697242,\\n37.7695189,\\n37.7696889,\\n37.7697153,\\n37.7701244,\\n37.7700604,\\n37.7705491,\\n37.7715705,\\n37.771752,\\n37.7707533,\\n37.769866];\\n\\nvar i = Math.floor((time/3 % 14000) / 1000);\\n\\nreturn lats[i];\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#4caf50\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var lons = [-122.4261215,\\n-122.4219157,\\n-122.4199623,\\n-122.4179074,\\n-122.4155876,\\n-122.4155521,\\n-122.4163203,\\n-122.4193876,\\n-122.4210496,\\n-122.422284,\\n-122.4232717,\\n-122.4235138,\\n-122.4247605,\\n-122.4258812];\\n\\nvar i = Math.floor((time/3 % 14000) / 1000);\\n\\nreturn lons[i];\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"mapProvider\":\"OpenStreetMap.Mapnik\",\"fitMapBounds\":true,\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"showLabel\":true,\"label\":\"${entityName}\",\"showTooltip\":true,\"tooltipColor\":\"#fff\",\"tooltipFontColor\":\"#000\",\"tooltipOpacity\":1,\"tooltipPattern\":\"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
End Time: ${maxTime}
Start Time: ${minTime}\",\"strokeWeight\":3,\"strokeOpacity\":1,\"pointSize\":10,\"markerImageSize\":34,\"rotationAngle\":180},\"title\":\"Trip Animation\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"legendConfig\":{\"position\":\"bottom\",\"showMin\":false,\"showMax\":false,\"showAvg\":false,\"showTotal\":false}}" + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Route\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var lats = [37.7696499,\\n37.7699074,\\n37.7699536,\\n37.7697242,\\n37.7695189,\\n37.7696889,\\n37.7697153,\\n37.7701244,\\n37.7700604,\\n37.7705491,\\n37.7715705,\\n37.771752,\\n37.7707533,\\n37.769866];\\n\\nvar i = Math.floor((time/3 % 14000) / 1000);\\n\\nreturn lats[i];\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#4caf50\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var lons = [-122.4261215,\\n-122.4219157,\\n-122.4199623,\\n-122.4179074,\\n-122.4155876,\\n-122.4155521,\\n-122.4163203,\\n-122.4193876,\\n-122.4210496,\\n-122.422284,\\n-122.4232717,\\n-122.4235138,\\n-122.4247605,\\n-122.4258812];\\n\\nvar i = Math.floor((time/3 % 14000) / 1000);\\n\\nreturn lons[i];\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"mapProvider\":\"OpenStreetMap.Mapnik\",\"fitMapBounds\":true,\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"showLabel\":true,\"label\":\"${entityName}\",\"showTooltip\":true,\"tooltipColor\":\"#fff\",\"tooltipFontColor\":\"#000\",\"tooltipOpacity\":1,\"tooltipPattern\":\"${entityName}\\n
\\nTime: ${formattedTs}\\nLatitude: ${latitude:7}\\nLongitude: ${longitude:7}\",\"strokeWeight\":3,\"strokeOpacity\":1,\"pointSize\":10,\"markerImageSize\":34,\"rotationAngle\":180},\"title\":\"Trip Animation\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"legendConfig\":{\"position\":\"bottom\",\"showMin\":false,\"showMax\":false,\"showAvg\":false,\"showTotal\":false}}" } } ] diff --git a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js index 311efebf24..e7dcad6299 100644 --- a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js +++ b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js @@ -17,10 +17,8 @@ import './trip-animation-widget.scss'; import template from "./trip-animation-widget.tpl.html"; import TbOpenStreetMap from '../openstreet-map'; import L from 'leaflet'; -//import tinycolor from 'tinycolor2'; import tinycolor from "tinycolor2"; import {fillPatternWithActions, isNumber, padValue, processPattern} from "../widget-utils"; -//import {fillPatternWithActions, isNumber, padValue, processPattern, fillPattern} from "../widget-utils"; (function () { // save these original methods before they are overwritten @@ -121,20 +119,17 @@ function tripAnimationWidget() { } /*@ngInject*/ -function tripAnimationController($document, $scope, $http, $timeout, $filter) { +function tripAnimationController($document, $scope, $http, $timeout, $filter, $sce) { let vm = this; - //const varsRegex = /\$\{([^\}]*)\}/g; - //let icon; vm.initBounds = true; vm.markers = []; vm.index = 0; vm.dsIndex = 0; - vm.isPlaying = false; vm.minTime = 0; vm.maxTime = 0; - vm.isPLaying = false; + vm.isPlaying = false; vm.trackingLine = { "type": "FeatureCollection", features: [] @@ -163,7 +158,7 @@ function tripAnimationController($document, $scope, $http, $timeout, $filter) { function initializeCallbacks() { vm.self.onDataUpdated = function () { - createUpdatePath(); + createUpdatePath(true); }; vm.self.onResize = function () { @@ -192,26 +187,54 @@ function tripAnimationController($document, $scope, $http, $timeout, $filter) { } vm.playMove = function (play) { - if (play && vm.isPLaying) return; - if (play || vm.isPLaying) vm.isPLaying = true; - if (vm.isPLaying) { - if (vm.index + 1 > vm.maxTime) return; - vm.index++; - vm.trips.forEach(function (trip) { - moveMarker(trip); - }); + if (play && vm.isPlaying) return; + if (play || vm.isPlaying) vm.isPlaying = true; + if (vm.isPlaying) { + moveInc(1); vm.timeout = $timeout(function () { vm.playMove(); }, 1000 / vm.speed) } }; + vm.moveNext = function () { + vm.stopPlay(); + moveInc(1); + } + + vm.movePrev = function () { + vm.stopPlay(); + moveInc(-1); + } + + vm.moveStart = function () { + vm.stopPlay(); + moveToIndex(vm.minTime); + } + + vm.moveEnd = function () { + vm.stopPlay(); + moveToIndex(vm.maxTime); + } vm.stopPlay = function () { - vm.isPLaying = false; - $timeout.cancel(vm.timeout); + if (vm.isPlaying) { + vm.isPlaying = false; + $timeout.cancel(vm.timeout); + } }; + function moveInc(inc) { + let newIndex = vm.index + inc; + moveToIndex(newIndex); + } + + function moveToIndex(newIndex) { + if (newIndex > vm.maxTime || newIndex < vm.minTime) return; + vm.index = newIndex; + recalculateTrips(); + } + function recalculateTrips() { vm.trips.forEach(function (value) { moveMarker(value); @@ -233,7 +256,7 @@ function tripAnimationController($document, $scope, $http, $timeout, $filter) { vm.utils = vm.self.ctx.$scope.$injector.get('utils'); vm.showTimestamp = vm.settings.showTimestamp !== false; - vm.ctx.$element = angular.element("#heat-map", vm.ctx.$container); + vm.ctx.$element = angular.element("#trip-animation-map", vm.ctx.$container); vm.defaultZoomLevel = 2; if (vm.ctx.settings.defaultZoomLevel) { if (vm.ctx.settings.defaultZoomLevel > 0 && vm.ctx.settings.defaultZoomLevel < 21) { @@ -257,6 +280,8 @@ function tripAnimationController($document, $scope, $http, $timeout, $filter) { let staticSettings = {}; vm.staticSettings = staticSettings; //Calculate General Settings + staticSettings.buttonColor = tinycolor(vm.widgetConfig.color).setAlpha(0.54).toRgbString(); + staticSettings.disabledButtonColor = tinycolor(vm.widgetConfig.color).setAlpha(0.3).toRgbString(); staticSettings.mapProvider = vm.ctx.settings.mapProvider || "OpenStreetMap.Mapnik"; staticSettings.latKeyName = vm.ctx.settings.latKeyName || "latitude"; staticSettings.lngKeyName = vm.ctx.settings.lngKeyName || "longitude"; @@ -268,10 +293,14 @@ function tripAnimationController($document, $scope, $http, $timeout, $filter) { staticSettings.useLabelFunction = vm.ctx.settings.useLabelFunction || false; staticSettings.showLabel = vm.ctx.settings.showLabel || false; staticSettings.useTooltipFunction = vm.ctx.settings.useTooltipFunction || false; - staticSettings.tooltipPattern = vm.ctx.settings.tooltipPattern || "${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
Start Time: ${maxTime}
End Time: ${minTime}"; - staticSettings.tooltipOpacity = vm.ctx.settings.tooltipOpacity || 1; - staticSettings.tooltipColor = vm.ctx.settings.tooltipColor ? tinycolor(vm.ctx.settings.tooltipColor).toHexString() : "#ffffff"; - staticSettings.tooltipFontColor = vm.ctx.settings.tooltipFontColor ? tinycolor(vm.ctx.settings.tooltipFontColor).toHexString() : "#000000"; + staticSettings.tooltipPattern = vm.ctx.settings.tooltipPattern || "${entityName}\n" + + "
\n" + + "Time: ${formattedTs}\n" + + "Latitude: ${latitude:7}\n" + + "Longitude: ${longitude:7}"; + staticSettings.tooltipOpacity = angular.isNumber(vm.ctx.settings.tooltipOpacity) ? vm.ctx.settings.tooltipOpacity : 1; + staticSettings.tooltipColor = vm.ctx.settings.tooltipColor ? tinycolor(vm.ctx.settings.tooltipColor).toRgbString() : "#ffffff"; + staticSettings.tooltipFontColor = vm.ctx.settings.tooltipFontColor ? tinycolor(vm.ctx.settings.tooltipFontColor).toRgbString() : "#000000"; staticSettings.pathColor = vm.ctx.settings.color ? tinycolor(vm.ctx.settings.color).toHexString() : "#ff6300"; staticSettings.pathWeight = vm.ctx.settings.strokeWeight || 1; staticSettings.pathOpacity = vm.ctx.settings.strokeOpacity || 1; @@ -351,16 +380,23 @@ function tripAnimationController($document, $scope, $http, $timeout, $filter) { } } - function configureTripSettings(trip) { + function configureTripSettings(trip, index, apply) { trip.settings = {}; trip.settings.color = calculateColor(trip); trip.settings.strokeWeight = vm.staticSettings.pathWeight; trip.settings.strokeOpacity = vm.staticSettings.pathOpacity; trip.settings.pointColor = vm.staticSettings.pointColor; trip.settings.pointSize = vm.staticSettings.pointSize; - trip.settings.labelText = calculateLabel(trip); - trip.settings.tooltipText = calculateTooltip(trip); trip.settings.icon = calculateIcon(trip); + if (apply) { + $timeout(() => { + trip.settings.labelText = calculateLabel(trip); + trip.settings.tooltipText = $sce.trustAsHtml(calculateTooltip(trip)); + },0,true); + } else { + trip.settings.labelText = calculateLabel(trip); + trip.settings.tooltipText = $sce.trustAsHtml(calculateTooltip(trip)); + } } function calculateLabel(trip) { @@ -464,7 +500,7 @@ function tripAnimationController($document, $scope, $http, $timeout, $filter) { return icon; } - function createUpdatePath() { + function createUpdatePath(apply) { if (vm.trips && vm.map) { vm.trips.forEach(function (trip) { if (trip.marker) { @@ -486,7 +522,7 @@ function tripAnimationController($document, $scope, $http, $timeout, $filter) { } let normalizedTimeRange = createNormalizedTime(vm.data, 1000); createNormalizedTrips(normalizedTimeRange, vm.datasources); - createTripsOnMap(); + createTripsOnMap(apply); if (vm.initBounds && !vm.initTrips) { vm.trips.forEach(function (trip) { vm.map.extendBounds(vm.map.bounds, trip.polyline); @@ -501,7 +537,6 @@ function tripAnimationController($document, $scope, $http, $timeout, $filter) { vm.map.fitBounds(vm.map.bounds); } - } function fillPattern(pattern, replaceInfo, currentNormalizedValue) { @@ -549,12 +584,20 @@ function tripAnimationController($document, $scope, $http, $timeout, $filter) { } }); for (let i = min_time; i < max_time; i += step) { - normalizedArray.push({ts: i}) + normalizedArray.push({ts: i, formattedTs: $filter('date')(i, 'medium')}); + } - if (normalizedArray[normalizedArray.length - 1] && normalizedArray[normalizedArray.length - 1].ts !== max_time) normalizedArray.push({ts: max_time}); + if (normalizedArray[normalizedArray.length - 1] && normalizedArray[normalizedArray.length - 1].ts !== max_time) { + normalizedArray.push({ts: max_time, formattedTs: $filter('date')(max_time, 'medium')}); + } } vm.maxTime = normalizedArray.length - 1; - vm.minTime = 0; + vm.minTime = vm.maxTime > 1 ? 1 : 0; + if (vm.index < vm.minTime) { + vm.index = vm.minTime; + } else if (vm.index > vm.maxTime) { + vm.index = vm.maxTime; + } return normalizedArray; } @@ -611,27 +654,43 @@ function tripAnimationController($document, $scope, $http, $timeout, $filter) { } el.latLngs.push(data.latLng); }); - addAngleForTip(el); + addAngleForTrip(el); }) } - function addAngleForTip(trip) { + function addAngleForTrip(trip) { if (trip.timeRange && trip.timeRange.length > 0) { trip.timeRange.forEach(function (point, index) { let nextPoint, prevPoint; nextPoint = index === (trip.timeRange.length - 1) ? trip.timeRange[index] : trip.timeRange[index + 1]; prevPoint = index === 0 ? trip.timeRange[0] : trip.timeRange[index - 1]; - point.h = findAngle(prevPoint[vm.staticSettings.latKeyName], prevPoint[vm.staticSettings.lngKeyName], nextPoint[vm.staticSettings.latKeyName], nextPoint[vm.staticSettings.lngKeyName]); - point.h += vm.staticSettings.rotationAngle; + let nextLatLng = { + lat: nextPoint[vm.staticSettings.latKeyName], + lng: nextPoint[vm.staticSettings.lngKeyName] + }; + let prevLatLng = { + lat: prevPoint[vm.staticSettings.latKeyName], + lng: prevPoint[vm.staticSettings.lngKeyName] + }; + if (nextLatLng.lat === prevLatLng.lat && nextLatLng.lng === prevLatLng.lng) { + if (angular.isNumber(prevPoint.h)) { + point.h = prevPoint.h; + } else { + point.h = vm.staticSettings.rotationAngle; + } + } else { + point.h = findAngle(prevLatLng.lat, prevLatLng.lng, nextLatLng.lat, nextLatLng.lng); + point.h += vm.staticSettings.rotationAngle; + } }); } } - function createTripsOnMap() { + function createTripsOnMap(apply) { if (vm.trips.length > 0) { vm.trips.forEach(function (trip) { if (trip.timeRange.length > 0 && trip.latLngs.every(el => angular.isDefined(el))) { - configureTripSettings(trip, vm.index); + configureTripSettings(trip, vm.index, apply); if (vm.staticSettings.showPoints) { trip.points = []; trip.latLngs.forEach(function (latLng) { @@ -648,14 +707,14 @@ function tripAnimationController($document, $scope, $http, $timeout, $filter) { } if (trip.timeRange && trip.timeRange.length && angular.isUndefined(trip.marker)) { - trip.marker = L.marker(trip.timeRange[vm.index].latLng).addTo(vm.map.map); + trip.marker = L.marker(trip.timeRange[vm.index].latLng); trip.marker.setZIndexOffset(1000); trip.marker.setIcon(vm.staticSettings.icon); trip.marker.setRotationOrigin('center center'); - // trip.marker.addTo(vm.map.map); - trip.marker.on('click', function () { - showHideTooltip(trip); - }); + trip.marker.on('click', function () { + showHideTooltip(trip); + }); + trip.marker.addTo(vm.map.map); moveMarker(trip); } } @@ -675,10 +734,10 @@ function tripAnimationController($document, $scope, $http, $timeout, $filter) { trip.marker.setZIndexOffset(1000); trip.marker.setIcon(vm.staticSettings.icon); trip.marker.setRotationOrigin('center center'); + trip.marker.on('click', function () { + showHideTooltip(trip); + }); trip.marker.addTo(vm.map.map); - trip.marker.on('click', function () { - showHideTooltip(trip); - }); trip.marker.update(); } diff --git a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.scss b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.scss index 1b6ae956c3..b4616214fc 100644 --- a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.scss +++ b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.scss @@ -14,93 +14,123 @@ * limitations under the License. */ -.heat-map-widget { +.trip-animation-widget { + position: relative; width: 100%; height: 100%; font-size: 16px; line-height: 24px; -} - -.heat-map-info-panel { - position: absolute; - top: 0; - right: 0; - z-index: 2; - background-color: rgba(0, 0, 0, .3); - border-bottom-left-radius: 5px; - .md-button { - min-width: auto; + .trip-animation-label-container { + height: 24px; } -} -.heat-map-tooltip { - position: absolute; - top: 47px; - right: 0; - z-index: 2; - padding: 10px; - background-color: #fff; - border-top-left-radius: 10px; - border-bottom-left-radius: 10px; - transition: .3s ease-in-out; - - &-hidden { - transform: translateX(100%); - } -} + .trip-animation-container { + position: relative; + z-index: 1; + flex: 1; + width: 100%; -.heat-map-title { - padding: 10px; -} + #trip-animation-map { + z-index: 1; + width: 100%; + height: 100%; -.heat-map-control-panel { - position: absolute; - bottom: 0; - z-index: 2; - box-sizing: border-box; - width: 100%; - padding-right: 70px; - padding-left: 20px; - background: rgba(0, 0, 0, .3); + .pointsLayerMarkerIcon { + border-radius: 50%; + } + } - md-slider-container { - button { - max-width: none; + .trip-animation-info-panel { + position: absolute; + top: 0; + right: 0; + z-index: 2; + pointer-events: none; - ng-md-icon { - width: 36px; - height: 36px; + .md-button { + top: 0; + left: 0; + width: 32px; + min-width: 32px; + height: 32px; + min-height: 32px; + padding: 0 0 2px; + margin: 2px; + line-height: 24px; - svg { - width: inherit; - height: inherit; + ng-md-icon { + width: 24px; + height: 24px; + + svg { + width: inherit; + height: inherit; + } } } } - .panel-timer { - max-width: none; - font-size: 20px; - font-weight: 600; + .trip-animation-tooltip { + position: absolute; + top: 38px; + right: 0; + z-index: 2; + padding: 10px; + background-color: #fff; + transition: .3s ease-in-out; + + &-hidden { + transform: translateX(110%); + } } } -} -.heat-map-container { - position: relative; - z-index: 1; - flex: 1; - width: 100%; -} + .trip-animation-control-panel { + position: relative; + box-sizing: border-box; + width: 100%; + padding-left: 10px; -#heat-map { - z-index: 1; - width: 100%; - height: 100%; + md-slider-container { + md-slider { + min-width: 80px; + } + + button.md-button.md-icon-button { + width: 44px; + min-width: 44px; + height: 44px; + min-height: 44px; + margin: 0; + line-height: 28px; - .pointsLayerMarkerIcon { - border-radius: 50%; + md-icon { + width: 28px; + height: 28px; + font-size: 28px; + + svg { + width: inherit; + height: inherit; + } + } + } + + md-select { + margin: 0; + } + } + + .panel-timer { + max-width: none; + padding-right: 250px; + padding-left: 90px; + margin-top: -20px; + font-size: 12px; + font-weight: 500; + text-align: center; + } } } diff --git a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.tpl.html b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.tpl.html index 5ffc6d56f9..498ca5cb71 100644 --- a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.tpl.html +++ b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.tpl.html @@ -15,40 +15,53 @@ limitations under the License. --> -
-
+
{{vm.trips[vm.activeTripIndex].settings.labelText}}
-
+
+
+
+ + + +
+
+
+
+
+ + fast_rewind + + + skip_previous + - - + + skip_next + + + fast_forward + + + + play_circle_outline + {{ speed}} - - + + + pause_circle_outline + -
{{vm.trips[vm.activeTripIndex].timeRange[vm.index].ts | date:'medium'}} -
-
-
-
-
- - - - - - -
-
+
{{vm.trips[vm.activeTripIndex].timeRange[vm.index].ts | date:'medium'}}
-
\ No newline at end of file +
From 3ab8430a474102189a36ba25357af45098307a94 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 28 Mar 2019 19:13:33 +0200 Subject: [PATCH 11/12] Add Ukrainian translation. --- ui/src/app/locale/locale.constant-de_DE.json | 3 +- ui/src/app/locale/locale.constant-en_US.json | 3 +- ui/src/app/locale/locale.constant-es_ES.json | 3 +- ui/src/app/locale/locale.constant-fa_IR.json | 3 +- ui/src/app/locale/locale.constant-fr_FR.json | 3 +- ui/src/app/locale/locale.constant-it_IT.json | 3 +- ui/src/app/locale/locale.constant-ja_JA.json | 19 +- ui/src/app/locale/locale.constant-ko_KR.json | 3 +- ui/src/app/locale/locale.constant-ru_RU.json | 3 +- ui/src/app/locale/locale.constant-tr_TR.json | 19 +- ui/src/app/locale/locale.constant-uk_UA.json | 2183 ++++++++++++++++++ ui/src/app/locale/locale.constant-zh_CN.json | 15 +- 12 files changed, 2227 insertions(+), 33 deletions(-) create mode 100644 ui/src/app/locale/locale.constant-uk_UA.json diff --git a/ui/src/app/locale/locale.constant-de_DE.json b/ui/src/app/locale/locale.constant-de_DE.json index bc241e28a5..ff55d1bee3 100644 --- a/ui/src/app/locale/locale.constant-de_DE.json +++ b/ui/src/app/locale/locale.constant-de_DE.json @@ -1577,7 +1577,8 @@ "es_ES": "Spanisch", "ja_JA": "Japanisch", "tr_TR": "Türkisch", - "fa_IR": "Persisch" + "fa_IR": "Persisch", + "uk_UA": "Ukrainisch" } } } \ 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 d1fe2c88c9..0f951d39c7 100644 --- a/ui/src/app/locale/locale.constant-en_US.json +++ b/ui/src/app/locale/locale.constant-en_US.json @@ -1584,7 +1584,8 @@ "es_ES": "Spanish", "ja_JA": "Japanese", "tr_TR": "Turkish", - "fa_IR": "Persian" + "fa_IR": "Persian", + "uk_UA": "Ukrainian" } } } \ 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 48ace08e6b..cc5dd7f405 100644 --- a/ui/src/app/locale/locale.constant-es_ES.json +++ b/ui/src/app/locale/locale.constant-es_ES.json @@ -1577,7 +1577,8 @@ "es_ES": "Español", "ja_JA": "Japonés", "tr_TR": "Turco", - "fa_IR": "Persa" + "fa_IR": "Persa", + "uk_UA": "Ucraniano" } } } \ 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 index de9b2a4f5e..80c8c421cb 100644 --- a/ui/src/app/locale/locale.constant-fa_IR.json +++ b/ui/src/app/locale/locale.constant-fa_IR.json @@ -1576,7 +1576,8 @@ "es_ES": "اسپانيولي", "ja_JA": "ژاپني", "tr_TR": "ترکي", - "fa_IR": "فارسي" + "fa_IR": "فارسي", + "uk_UA": "اوکراین" } } } diff --git a/ui/src/app/locale/locale.constant-fr_FR.json b/ui/src/app/locale/locale.constant-fr_FR.json index f22f90548b..e10dd54c08 100644 --- a/ui/src/app/locale/locale.constant-fr_FR.json +++ b/ui/src/app/locale/locale.constant-fr_FR.json @@ -1014,7 +1014,8 @@ "zh_CN": "Chinois", "ja_JA": "Japonaise", "tr_TR": "Turc", - "fa_IR": "Persane" + "fa_IR": "Persane", + "uk_UA": "Ukrainien" } }, "layout": { diff --git a/ui/src/app/locale/locale.constant-it_IT.json b/ui/src/app/locale/locale.constant-it_IT.json index 986fdc19a0..e2cdb9c3ab 100644 --- a/ui/src/app/locale/locale.constant-it_IT.json +++ b/ui/src/app/locale/locale.constant-it_IT.json @@ -1582,7 +1582,8 @@ "es_ES": "Spagnolo", "ja_JA": "Giapponese", "tr_TR": "Turco", - "fa_IR": "Persiana" + "fa_IR": "Persiana", + "uk_UA": "Ucraino" } } } diff --git a/ui/src/app/locale/locale.constant-ja_JA.json b/ui/src/app/locale/locale.constant-ja_JA.json index 9fe6971ff9..9345359109 100644 --- a/ui/src/app/locale/locale.constant-ja_JA.json +++ b/ui/src/app/locale/locale.constant-ja_JA.json @@ -1205,10 +1205,10 @@ "tenant-required": "テナントが必要です" }, "timeinterval": { - "seconds-interval": "{ seconds, select, 1 {1 second} other {# seconds} }", - "minutes-interval": "{ minutes, select, 1 {1 minute} other {# minutes} }", - "hours-interval": "{ hours, select, 1 {1 hour} other {# hours} }", - "days-interval": "{ days, select, 1 {1 day} other {# days} }", + "seconds-interval": "{ seconds, plural, 1 {1 second} other {# seconds} }", + "minutes-interval": "{ minutes, plural, 1 {1 minute} other {# minutes} }", + "hours-interval": "{ hours, plural, 1 {1 hour} other {# hours} }", + "days-interval": "{ days, plural, 1 {1 day} other {# days} }", "days": "日々", "hours": "時間", "minutes": "分", @@ -1216,10 +1216,10 @@ "advanced": "上級" }, "timewindow": { - "days": "{ days, select, 1 { day } other {# days } }", - "hours": "{ hours, select, 0 { hour } 1 {1 hour } other {# hours } }", - "minutes": "{ minutes, select, 0 { minute } 1 {1 minute } other {# minutes } }", - "seconds": "{ seconds, select, 0 { second } 1 {1 second } other {# seconds } }", + "days": "{ days, plural, 1 { day } other {# days } }", + "hours": "{ hours, plural, 0 { hour } 1 {1 hour } other {# hours } }", + "minutes": "{ minutes, plural, 0 { minute } 1 {1 minute } other {# minutes } }", + "seconds": "{ seconds, plural, 0 { second } 1 {1 second } other {# seconds } }", "realtime": "リアルタイム", "history": "歴史", "last-prefix": "最終", @@ -1460,7 +1460,8 @@ "es_ES": "スペイン語", "ja_JA": "日本語", "tr_TR": "トルコ語", - "fa_IR": "ペルシャ語" + "fa_IR": "ペルシャ語", + "uk_UA": "ウクライナ語" } } } \ 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 87f92dc3e4..7ef4935df5 100644 --- a/ui/src/app/locale/locale.constant-ko_KR.json +++ b/ui/src/app/locale/locale.constant-ko_KR.json @@ -1336,7 +1336,8 @@ "it_IT": "이탈리아 사람", "ja_JA": "일본어", "tr_TR": "터키어", - "fa_IR": "페르시아 인" + "fa_IR": "페르시아 인", + "uk_UA": "우크라이나의" } } } \ 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 2d05051de8..7477c6d70e 100644 --- a/ui/src/app/locale/locale.constant-ru_RU.json +++ b/ui/src/app/locale/locale.constant-ru_RU.json @@ -1576,7 +1576,8 @@ "tr_TR": "Турецкий", "fr_FR": "Французский", "ja_JA": "Японский", - "fa_IR": "Персидский" + "fa_IR": "Персидский", + "uk_UA": "Украинский" } } } \ 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 e65d07bd83..52dc6cc10a 100644 --- a/ui/src/app/locale/locale.constant-tr_TR.json +++ b/ui/src/app/locale/locale.constant-tr_TR.json @@ -1285,10 +1285,10 @@ "tenant-required": "Tenant gerekli" }, "timeinterval": { - "seconds-interval": "{ seconds, select, 1 {1 saniye} other {# saniye} }", - "minutes-interval": "{ minutes, select, 1 {1 dakika} other {# dakika} }", - "hours-interval": "{ hours, select, 1 {1 saat} other {# saat} }", - "days-interval": "{ days, select, 1 {1 gün} other {# gün} }", + "seconds-interval": "{ seconds, plural, 1 {1 saniye} other {# saniye} }", + "minutes-interval": "{ minutes, plural, 1 {1 dakika} other {# dakika} }", + "hours-interval": "{ hours, plural, 1 {1 saat} other {# saat} }", + "days-interval": "{ days, plural, 1 {1 gün} other {# gün} }", "days": "Gün", "hours": "Saat", "minutes": "Dakika", @@ -1296,10 +1296,10 @@ "advanced": "İleri düzey" }, "timewindow": { - "days": "{ days, select, 1 { gün } other {# gün } }", - "hours": "{ hours, select, 0 { saat } 1 {1 saat } other {# saat } }", - "minutes": "{ minutes, select, 0 { dakika } 1 {1 dakika } other {# dakika } }", - "seconds": "{ seconds, select, 0 { saniye } 1 {1 saniye } other {# saniye } }", + "days": "{ days, plural, 1 { gün } other {# gün } }", + "hours": "{ hours, plural, 0 { saat } 1 {1 saat } other {# saat } }", + "minutes": "{ minutes, plural, 0 { dakika } 1 {1 dakika } other {# dakika } }", + "seconds": "{ seconds, plural, 0 { saniye } 1 {1 saniye } other {# saniye } }", "realtime": "Gerçek zaman", "history": "Tarih", "last-prefix": "son", @@ -1542,7 +1542,8 @@ "es_ES": "İspanyol", "ja_JA": "Japonca", "tr_TR": "Türkçe", - "fa_IR": "Farsça" + "fa_IR": "Farsça", + "uk_UA": "Ukrayna" } } } \ No newline at end of file diff --git a/ui/src/app/locale/locale.constant-uk_UA.json b/ui/src/app/locale/locale.constant-uk_UA.json new file mode 100644 index 0000000000..4eb59a2d40 --- /dev/null +++ b/ui/src/app/locale/locale.constant-uk_UA.json @@ -0,0 +1,2183 @@ + { + "access": { + "unauthorized": "Неавторизований", + "unauthorized-access": "Неавторизований доступ", + "unauthorized-access-text": "Щоб отримати доступ до цього ресурсу, потрібно ввійти в систему!", + "access-forbidden": "Доступ заборонено", + "access-forbidden-text": "Недостатньо прав для доступу!
Спробуйте увійти як інший користувач, якщо ви все ще хочете отримати доступ до цього ресурсу.", + "refresh-token-expired": "Дані про сесію застарілі", + "refresh-token-failed": "Не вдається відновити сеанс" + }, + "action": { + "activate": "Активувати", + "suspend": "Призупинити", + "save": "Зберегти", + "saveAs": "Зберегти як", + "cancel": "Скасувати", + "ok": "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}}", + "move": "Перемістити", + "select": "Вибрати" + }, + "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": "Надіслати тестове повідомлення", + "use-system-mail-settings": "Використовувати параметри системного поштового сервера", + "mail-templates": "Шаблони електронної пошти", + "mail-template-settings": "Налаштування шаблонів електронної пошти", + "use-system-mail-template-settings": "Використовувати шаблони системної електронної пошти", + "mail-template": { + "mail-template": "Шаблон електронної пошти", + "test": "Тестове повідомлення", + "activation": "Повідомлення про активацію рахунку", + "account-activated": "Обліковий запис активовано", + "reset-password": "Відновити повідомлення пароля", + "password-was-reset": "Пароль було надіслано повідомленням" + }, + "mail-subject": "Тема повідомлення", + "mail-body": "Вміст повідомлення" + }, + "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": "Інтервал опитування (сек)", + "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-group": "Група сутностей", + "filter-type-entity-list": "Список сутностей", + "filter-type-entity-name": "Назва сутності", + "filter-type-entity-group-list": "Список груп сутностей", + "filter-type-entity-group-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": "Тип активів '{{assetType}}' і ім'я, що починаються з '{{prefix}}'", + "filter-type-device-type": "Тип пристрою", + "filter-type-device-type-description": "Тип пристроїв '{{deviceType}}'", + "filter-type-device-type-and-name-description": "Тип пристроїв '{{deviceType}}' і ім'я, що починаються з '{{prefix}}'", + "filter-type-entity-view-type": "Тип перегляду сутності", + "filter-type-entity-view-type-description": "Перегляд сутності з типом '{{entityView}}'", + "filter-type-entity-view-type-and-name-description": "Перегляд сутності з типом'{{entityView}}' і іменем, що починаються з '{{prefix}}'", + "filter-type-relations-query": "Запит відносин", + "filter-type-relations-query-description": "{{entities}}, які мають {{relationType}} відношення {{direction}} {{rootEntity}}", + "filter-type-asset-search-query": "Запит пошуку активу", + "filter-type-asset-search-query-description": "Активи з типами {{assetTypes}}, які мають {{relationType}} відношення {{direction}} {{rootEntity}}", + "filter-type-device-search-query": "Запит пошуку пристрою", + "filter-type-device-search-query-description": "Пристрої з типами {{deviceTypes}}, які мають {{relationType}} відношення {{direction}} {{rootEntity}}", + "filter-type-entity-view-search-query": "Запит пошуку переглядів сутностей", + "filter-type-entity-view-search-query-description": "Перегляд сутності з типами {{entityViewTypes}}, які мають {{relationType}} відношення {{direction}} {{rootEntity}}", + "entity-filter": "Фільтр сутності", + "resolve-multiple": "Як декілька сутностей", + "filter-type": "Тип фільтра", + "filter-type-required": "Необхідно вказати тип фільтра.", + "entity-filter-no-entity-matched": "Не знайдено жодних сутностей, які відповідають вказаному фільтру.", + "no-entity-filter-specified": "No entity filter specified", + "root-state-entity": "Use dashboard state entity as root", + "group-state-entity": "Use dashboard state entity as entity group", + "root-entity": "Root entity", + "state-entity-parameter-name": "State entity parameter name", + "default-state-entity": "Default state entity", + "default-state-entity-group": "Default state entity group", + "default-entity-parameter-name": "By default", + "max-relation-level": "Max relation level", + "unlimited-level": "Unlimited level", + "state-entity": "Dashboard state entity", + "entities-of-group-state-entity": "Entities from dashboard state entity group", + "all-entities": "All entities", + "any-relation": "any" + }, + "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": "Назва активу починається з", + "selected-assets": "{ count, plural, 1 {1 актив} other {# активи} } selected", + "search": "Пошук активів", + "select-group-to-add": "Виберіть цільову групу, щоб додати вибрані активи", + "select-group-to-move": "Виберіть цільову групу для переміщення вибраних активів", + "remove-assets-from-group": "Ви впевнені, що хочете видалити { count, plural, 1 {1 актив} other {# актив} } з групи '{entityGroup}'?", + "group": "Група активів", + "list-of-groups": "{ count, plural, 1 {Одна група активів} other {Список # груп активів} }", + "group-name-starts-with": "Групи активів, чиї імена починаються з '{{prefix}}'" + }, + "attribute": { + "attributes": "Атрибути", + "latest-telemetry": "Остання телеметрія", + "attributes-scope": "Область видимості атрибутів", + "scope-latest-telemetry": "Остання телеметрія", + "scope-client": "Клієнтські атрибути", + "scope-server": "Серверні атрибути", + "scope-shared": "Спільні атрибути", + "add": "Додати атрибут", + "add-attribute-prompt": "Будь ласка, додайте атрибут", + "key": "Ключ", + "last-update-time": "Останнє оновлення", + "key-required": "Ключ атрибута обов'язковий.", + "value": "Значення", + "value-required": "Значення атрибута обов'язкове.", + "delete-attributes-title": "Ви впевнені, що хочете видалити { count, plural, 1 {1 attribute} other {# attributes} }?", + "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 attribute} other {# attributes} } selected ...вибрані вибрати", + "selected-telemetry": "{ count, plural, 1 {1 telemetry unit} other {# telemetry units} } selected" + }, + "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-added-to-entity-group": "Додано до групи", + "type-removed-from-entity-group": "Вилучено з групи", + "type-relation-add-or-update": "Відношення оновлено", + "type-relation-delete": "Відношення видалено", + "type-relations-delete": "Всі відношення видалено", + "type-alarm-ack": "Визнано", + "type-alarm-clear": "Очищено", + "type-rest-api-rule-engine-call": "Rule engine REST API call", + "status-success": "Успішно", + "status-failure": "Невдало", + "audit-log-details": "Подробиці журналу операцій", + "no-audit-logs-prompt": "Жодних журналів операцій не знайдено", + "action-data": "Дані про дії", + "failure-details": "Невдалі подробиці", + "search": "Пошук журналів перевірки", + "clear-search": "Очистити пошук" + }, + "confirm-on-exit": { + "message": "У вас є незбережені зміни. Ви впевнені, що хочете залишити цю сторінку?", + "html-message": "У вас є незбережені зміни.
Ви впевнені, що хочете залишити цю сторінку?", + "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": "Введіть пошук" + }, + "converter": { + "converter": "Перетворювач даних", + "converters": "Перетворювачі даних", + "select-converter": "Виберіть перетворювач даних", + "no-converters-matching": "Не має перетворювачів даних, які відповідають '{{entity}}'.", + "converter-required": "Необхідно вказати перетворювач даних", + "delete": "Видалити перетворювач даних", + "management": "Управління перетворювачами даних", + "add-converter-text": "Додати новий перетворювач даних", + "no-converters-text": "Перетворювачів даних не знайдено", + "selected-converters": "{ count, plural, 1 {1 перетворювач даних} other {# перетворювачі даних} } вибраний", + "delete-converter-title": "Ви впевнені, що хочете видалити перетворювач даних '{{converterName}}'?", + "delete-converter-text": "Будьте обережні, після підтвердження, перетворювач даних та всі пов'язані з ним дані,стануть недоступними).", + "delete-converters-title": "Ви впевнені, що хочете видалити{ count, plural, 1 {1 перетворювач даних} other {# перетворювачі даних} }?", + "delete-converters-action-title": "Видалити { count, plural, 1 {1 перетворювач даних} other {# перетворювачі даних} }", + "delete-converters-text": "Будьте обережні, після підтвердження всі вибрані перетворювачі даних буде видалено, і всі пов'язані з ними дані буде втрачено.", + "events": "Події", + "add": "Додати перетворювач даних", + "converter-details": "Подробиці про перетворювач даних", + "details": "Подробиці", + "copyId": "Копіювати Id перетворювача даних", + "idCopiedMessage": "Id перетворювача даних було скопійовано у буфер обміну", + "debug-mode": "Режим налагодження", + "name": "Ім'я", + "name-required": "Ім'я обов'язкове.", + "description": "Опис", + "decoder": "Декодер", + "encoder": "Кодер", + "test-decoder-fuction": "Тестування функції декодера", + "test-encoder-fuction": "Тестування функції кодера", + "decoder-input-params": "Параметри введення декодера", + "encoder-input-params": "Параметри введення кодера", + "payload": "Вхідне повідомлення", + "payload-content-type": "Тип контенту вхідного повідомлення", + "payload-content": "Зміст вхідного повідомлення", + "message": "Повідомлення", + "message-type": "Тип повідомлення", + "message-type-required": "Необхідно задати тип повідомлення", + "test": "Тест", + "metadata": "Метадані", + "metadata-required": "Записи метаданих не можуть бути порожніми.", + "integration-metadata": "Інтеграція метаданих", + "integration-metadata-required": "Записи інтеграції метаданих не можуть бути порожніми.", + "output": "Вихідні дані", + "import": "Імпорт перетворювача даних", + "export": "Експорт перетворювача даних", + "export-failed-error": "Неможливо експортувати перетворювач даних: {{помилка}}", + "create-new-converter": "Створити новий перетворювач даних", + "converter-file": "Файл перетворювача даних(конвектер файл)", + "invalid-converter-file-error": "Неможливо імпортувати перетворювач даних: недійсна структура даних перетворювача.", + "type": "Тип", + "type-required": "Необхідно задати тип.", + "type-uplink": "Від пристрою", + "type-downlink": "До пристрою" + }, + "content-type": { + "json": "Json", + "text": "Текст", + "binary": "Бінарний (Base64)" + }, + "customer": { + "customer": "Клієнт", + "customers": "Клієнти", + "management": "Клієнтський менеджмент", + "dashboard": "Інформаційна панель клієнта", + "dashboards": "Інформаційні панелі клієнта", + "devices": "Пристрої клієнта", + "entity-views": "Представлення сутностей", + "assets": "Клієнтські активи", + "public-dashboards": "Публічні інформаційні панелі", + "public-devices": "Публічні пристрої", + "public-assets": "Публічні активи", + "public-entity-views": "Публічне представлення сутностей 440", + "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 клієнт}, інші {# клієнти}}?", + "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": "Необхідно задати клієнта", + "selected-customers": "{ count, plural, 1 {1 клієнт} інші {# клієнти} } вибрано", + "search": "Пошук клієнтів", + "select-group-to-add": "Виберіть цільову групу, щоб додати вибраних клієнтів", + "select-group-to-move": "Виберіть цільову групу для переміщення вибраних клієнтів", + "remove-customers-from-group": "Ви впевнені, що хочете видалити{ count, plural, 1 {1 клієнт} other {# клієнти} } з групи'{entityGroup}'?", + "group": "Група клієнтів", + "list-of-groups": "{ count, plural, 1 {Одна група клієнтів} other {Список # груп клієнтів} }", + "group-name-starts-with": "Групи клієнтів, імена яких починаються з '{{prefix}}'", + "select-default-customer": "Виберати клієнта за замовчуванням", + "default-customer": "Клієнт за замовчуванням", + "default-customer-required": "Необхідно вказати клієнта за замовчуванням для налагодження панелі візуалізації на рівні замовника", + "allow-white-labeling": "Дозволити брендування" + }, + "custom-translation": { + "custom-translation": "Переклад для користувача", + "translation-map": "Карта перекладу", + "key": "Ключ перекладу", + "import": "Імпорт перекладу", + "export": "Експорт перекладу", + "export-data": "Дані про експорт перекладу", + "import-data": "Дані про імпорт перекладу", + "translation-file": "Файл перекладу", + "invalid-translation-file-error": "Неможливо імпортувати файл перекладу: недійсна структура даних перекладу.", + "custom-translation-hint": "Визначте індивідуальний переклад в JSON нижче. Цей JSON перезапише переклад за замовчуванням. Натисніть 'Завантажити файл перекладу', щоб отримати існуючий переклад. Ви також можете скористатись завантаженим файлом як посиланням на наявні пари параметрів перекладу ключ-значення.", + "download-locale-file": "Завантажити файл перекладу" + }, + "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": "Ви впевнені, що хочете видалити панель приладів '{{назва панелі приладів}}'?", + "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": "Ви впевнені, що хочете позбавити панелі приладів '{{назва інформаційної панелі}}'?", + "unassign-dashboard-text": "Після підтвердження, клієнт буде позбавлений панелі приладів. Панель приладів і пов'язані з нею дані будуть недоступні клієнтові.", + "unassign-dashboard": "Позбавити панелі приладів", + "unassign-dashboards-title": "Ви впевнені, що хочете позбавити { count, plural, 1 {1 панелі приладів} other {# панелей приладів} }?", + "unassign-dashboards-text": "Після підтвердження, клієтн буде позбавлений усіх вибраних панелей приладів і даних, які з ними пов'язані.", + "public-dashboard-title": "Панель приладів тепер публічна", + "public-dashboard-text": "Ваша панель приладів {{dashboardTitle}} тепер публічна і доступна іншим link:", + "public-dashboard-notice": "Note: Не забудьте зробити спільні пристрої загальнодоступними, щоб отримати доступ до їхніх даних.", + "make-private-dashboard-title": "Ви впевнені, що хочете зробити панель приладів '{{назва панелі приладів}}' приватною?", + "make-private-dashboard-text": "Після підтвердження панель приладів стане приватною і не буде доступною іншим.", + "make-private-dashboard": "Зробити панель приладів приватною", + "socialshare-text": "'{{dashboardTitle}}' powered by ThingsBoard", + "socialshare-title": "'{{dashboardTitle}}' powered by ThingsBoard", + "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}}", + "export-pdf": "Експортувати як PDF", + "export-png": "Експортувати як PNG", + "export-jpg": "Експортувати як JPEG", + "export-json-config": "Експортувати конфігурацію JSON", + "download-dashboard-progress": "Генерування панелі візуалізації {{reportType}} ...", + "create-new-dashboard": "Створити нову панель візуалізації", + "dashboard-file": "Файл панелі візуалізації", + "invalid-dashboard-file-error": "Неможливо імпортувати панель візуалізації: неправильна структура даних панелі візуалізації.", + "dashboard-import-missing-aliases-title": "Configure aliases used by imported dashboard Налаштування псевдонімів, що використовуються імпортованою панеллю візуалізації", + "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": "Неможливо знайти пристрої, які відповідають певному фільтру псевдонімів.
Зверніться до свого адміністратора, щоб вирішити цю проблему.", + "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 dashboard state} other {# dashboard states} } вибрано", + "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": "Maximum { count, plural, 1 {1 function type is allowed.} other {# function types are allowed} }", + "time-description": "мітка часу поточного значення;", + "value-description": "поточне значення;", + "prev-value-description": "результат попереднього виклику функції;", + "time-prev-description": "мітка часу попереднього значення;", + "prev-orig-value-description": "оригінальне попереднє значення;" + }, + "datasource": { + "type": "Тип джерела даних", + "name": "Ім'я", + "add-datasource-prompt": "Додайте джерело даних" + }, + "details": { + "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": "Псевдонім з таким іменем '{{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": "Псевдонім пристрою '{{deviceAlias}}' не може бути видалений, оскільки він використовується наступним(и) віджетом(ами):
{{widgetsList}}", + "is-gateway": "Шлюз", + "public": "Публічно", + "device-public": "Пристрій є публічним", + "select-device": "Виберіть пристрій", + "selected-devices": "{ count, plural, 1 {1 пристрій} other {# пристрої} } вибрано", + "search": "Шукати пристрої", + "select-group-to-add": "Виберіть цільову групу, щоб додати вибраний пристрій", + "select-group-to-move": "Виберіть цільову групу для переміщення вибраних пристроїв", + "remove-devices-from-group": "Ви впевнені, що хочете видалити { count, plural, 1 {1 пристрій} other {# пристрої} } з групи '{entityGroup}'?", + "group": "Група пристроїв", + "list-of-groups": "{ count, plural, 1 {Одна група пристроїв} other {Список # груп пристроїв} }", + "group-name-starts-with": "Групи пристроїв, імена яких починаються з '{{prefix}}'" + }, + "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": "Псевдонім сутності'{{entityAlias}}' неможливо видалити, так як це використовується наступним віджетом(s):
{{widgetsList}}", + "duplicate-alias-error": "Знайдено повторюваний псевдонім '{{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 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-scheduler-event": "Scheduler event", + "type-scheduler-events": "Scheduler events", + "list-of-scheduler-events": "{ count, plural, 1 {One scheduler event} other {List of # scheduler events} }", + "scheduler-event-name-starts-with": "Scheduler events whose names start with '{{prefix}}'", + "type-blob-entity": "Blob entity", + "type-blob-entities": "Blob entities", + "list-of-blob-entities": "{ count, plural, 1 {One blob entity} other {List of # blob entities} }", + "blob-entity-name-starts-with": "Blob entities whose names start 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": "Стовпці для відображення", + "type-entity-group": "Група сутностей", + "type-converter": "Перетворювач даних", + "type-converters": "Перетворювачі даних", + "list-of-converters": "{ count, plural, 1 {Однин перетворювач даних} other {Список # перетворювачів даних} }", + "converter-name-starts-with": "Перетворювачі даних, імена яких починаються з '{{prefix}}'", + "type-integration": "Інтеграція", + "type-integrations": "Інтеграції", + "list-of-integrations": "{ count, plural, 1 {Одна інтеграція} other {Список # інтеграцій} }", + "integration-name-starts-with": "Інтеграції, імена яких починаються з '{{prefix}}'" + }, + "entity-group": { + "entity-group": "Група сутності", + "details": "Деталі", + "columns": "Стовпці", + "add-column": "Додати стовпець", + "column-value": "Значення", + "column-value-required": "Необхідно вказати значення.", + "column-title": "Назва", + "default-sort-order": "Основний порядок сортування", + "default-sort-order-required": "Необхідно вказати основний порядок сортування.", + "hide-in-mobile-view": "Мобільний приховано", + "use-cell-style-function": "Використовувати функцію стилю комірки", + "use-cell-content-function": "Use cell content function", + "edit-column": "Редагувати стовпець", + "column-details": "Деталі стовпця", + "actions": "Дії", + "settings": "Налаштування", + "delete": "Видалити групу сутностей", + "name": "Ім'я", + "name-required": "Необхідно вказати ім'я.", + "description": "Опис", + "add": "Додати групу сутностей", + "add-entity-group-text": "Додати нову групу сутностей", + "no-entity-groups-text": "Не знайдено жодних груп сутності", + "entity-group-details": "Деталі групи сутності", + "delete-entity-groups": "Видалити групи сутностей", + "delete-entity-group-title": "Ви впевнені, що хочете видалити групу сутності '{{entityGroupName}}'?", + "delete-entity-group-text": "Будьте обережні, після підтвердження, група сутностей і всі пов'язані з нею дані стануть недоступними.", + "delete-entity-groups-title": "Ви впевнені, що хочете видалити { count, plural, 1 {1 групу сутності} other {# групи сутностей} }?", + "delete-entity-groups-action-title": "Видалити { count, plural, 1 {1 групу сутності} other {# групи сутностей} }", + "delete-entity-groups-text": "Будьте обережні, після підтвердження, всі виділені групи сутностей і пов'язані з ними дані, стануть недоступними.", + "device-groups": "Групи пристроїв", + "asset-groups": "Групи активів", + "customer-groups": "Групи клієнтів", + "device-group": "Група пристроїв", + "asset-group": "Група активів", + "customer-group": "Група клієнтів", + "fetch-more": "Отримати більше", + "column-type": { + "column-type": "Тип стовпця", + "client-attribute": "Атрибут клієнта", + "shared-attribute": "Спільний атрибут", + "server-attribute": "Атрибут сервера", + "timeseries": "Телеметрія", + "entity-field": "Поле сутності" + }, + "column-type-required": "Необхідно вказати тип стовпця.", + "entity-field": { + "created-time": "Створений час", + "name": "Ім'я", + "type": "Тип", + "assigned_customer": "Призначений клієнт", + "authority": "Авторитет", + "first_name": "Ім'я", + "last_name": "Прізвище", + "email": "Електронна пошта", + "title": "Назва", + "country": "Країна", + "state": "Штат", + "city": "Місто", + "address": "Адреса", + "address2": "Адреса 2", + "zip": "Zip", + "phone": "Телефон" + }, + "sort-order": { + "asc": "У порядку зростання", + "desc": "У порядку зменшення", + "none": "Не має" + }, + "details-mode": { + "on-row-click": "Клацніть на рядок", + "on-action-button-click": "Клацніть на кнопку детелі", + "disabled": "Вимкнено" + }, + "add-to-group": "Додати до групи", + "move-to-group": "Перемістити до групи", + "select-entity-group": "Виберати групу сутностей", + "no-entity-groups-matching": "Не знайдено жодних груп сутностей, що відповідають '{{entityGroup}}'.", + "target-entity-group-required": "Необхідно вказати цільову групу сутності.", + "remove-from-group": "Видалити з групи", + "group-table-title": "Group table title", + "enable-search": "Увімкнути пошук сутностей", + "enable-add": "Увімкнути додавання сутностей", + "enable-delete": "Увімкнути видалення сутностей", + "enable-selection": "Увімкнути вибір сутностей", + "enable-group-transfer": "Увімкнути дії групового перенесення", + "display-pagination": "Відображення сторінок", + "default-page-size": "Розмір сторінки за замовчуванням", + "enable-assignment-actions": "Увімкнути дії призначення", + "enable-credentials-management": "Увімкнути керування авторизаційними даними", + "enable-users-management": "Увімкнути керування користувачами", + "enable-assets-management": "Увімкнути керування активами", + "enable-devices-management": "Увімкнути керування пристроями", + "enable-dashboards-management": "Увімкнути керування панелями візуалізації", + "open-details-on": "Відкрити деталі сутності", + "select-existing": "Виберіть існуючу групу сутностей", + "create-new": "Створити нову групу сутностей", + "new-entity-group-name": "Нове ім'я групи сутностей", + "entity-group-list": "Список групи сутностей", + "entity-group-list-empty": "Не вибрано жодної групи сутностей.", + "name-starts-with": "Назва групи сутностей починається з", + "entity-group-name-filter-required": "Необхідно задати назву групи сутностей." + }, + "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": "Псевдонім з такою назвою вже існує '{{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": "Не вдалося видалити псевдонім пристрою'{{entityViewAlias}}', так як він використовується наступним(и) віджетом(ами):
{{widgetsList}}", + "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": "Перегляд сутностей автоматично копіюватиме вказані атрибути з цільової сутності кожного разу, коли ви зберігаєте або оновлюєте ці перегляди. В цілях продуктивності, атрибути цільової сутності не поширюються на представлення сутності на кожній зміні їх атрибутів. Можна ввімкнути автоматичне поширення, налаштувавши у вашому ланцюжку правило \"copy to view\" і пов'язуючи його з повідомленнями типу \"Post attributes\" і \"Attributes Updated\"..", + "timeseries-data": "Дані телеметрії", + "timeseries-data-hint": "Налаштуйте ключі даних телеметрії цільової сутності, які будуть доступні перегляду сутності. Ці дані доступні лише для читання." + }, + "event": { + "events": "Події", + "event-type": "Тип події", + "type-error": "Помилка", + "type-lc-event": "Подія життєвого циклу", + "type-stats": "Статистика", + "type-debug-converter": "Налагоджувати", + "type-debug-integration": "Налагоджувати", + "type-debug-rule-node": "Налагоджувати", + "type-debug-rule-chain": "Налагоджувати", + "no-events-prompt": "Не знайдено жодних подій", + "error": "Помилка", + "alarm": "Сигнал тривоги", + "event-time": "Час події", + "server": "Сервер", + "body": "Тіло", + "method": "Метод", + "type": "Тип", + "in": "In", + "out": "Out", + "metadata": "Метадані", + "message": "Повідомлення", + "entity": "Сутність", + "message-id": "Id повідомлення", + "message-type": "Тип повідомлення", + "data-type": "Тип даних", + "relation-type": "Тип зв'язку", + "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": "Field is 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": "Json вираз для id запиту", + "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 вузла пристрою", + "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": "Unit ID", + "modbus-unit-id-range": "Unit ID should be in a range from 1 to 247.", + "modbus-device-name": "Ім'я пристрою", + "modbus-poll-period": "Період опитування (мс)", + "modbus-attributes-poll-period": "Період опитування атрибутів (мс)", + "modbus-timeseries-poll-period": "Період опитування телеметрії (мс)", + "modbus-poll-period-range": "Період опитування повинен бути більше 0.", + "modbus-tag": "Тег", + "modbus-function": "Modbus функція", + "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": "Рахунок регістру повинен бути більше 0.", + "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": "Видалити елементи", + "scroll-to-top": "Перейти угору" + }, + "help": { + "goto-help-page": "Перейти на сторінку довідки" + }, + "home": { + "home": "Домашня сторінка", + "profile": "Профіль", + "logout": "Вийти", + "menu": "Меню", + "avatar": "Аватар", + "open-user-menu": "Відкрити меню користувача" + }, + "import": { + "no-file": "Не вибрано жодного файлу", + "drop-file": "Перетягніть JSON файл, або клацніть, щоб вибрати файл для завантаження.", + "drop-csv-file": "Перетягніть CSV файл, або клацніть, щоб вибрати файл для завантаження." + }, + "integration": { + "integration": "Інтеграція", + "integrations": "Інтеграції", + "select-integration": "Виберіть інтеграцію", + "no-integrations-matching": "Не знайдено жодних інтеграцій, які відповідають '{{entity}}'.", + "integration-required": "Необхідно вказати інтеграцію", + "delete": "Видалити інтеграцію", + "management": "Управління інтеграціями", + "add-integration-text": "Додати нову інтеграцію", + "no-integrations-text": "Не знайдено жодної інтеграції", + "selected-integrations": "{ count, plural, 1 {1 інтеграція} other {# інтеграції} } вибрано", + "delete-integration-title": "Ви впевнені, що хочете видалити інтеграцію '{{integrationName}}'?", + "delete-integration-text": "Будьте обережні, після підтвердження інтеграція та всі пов'язані з нею дані стануть недоступними.", + "delete-integrations-title": "Ви впевнені, що хочете видалити { count, plural, 1 {1 інтеграцію} other {# інтеграції} }?", + "delete-integrations-action-title": "Видалити { count, plural, 1 {1 інтеграцію} other {# інтеграції} }", + "delete-integrations-text": "Будьте обережні, після підтвердження всі вибрані інтеграції будуть видалені, і всі пов'язані з ними дані стануть недоступними.", + "events": "Події", + "add": "Додати інтеграцію", + "integration-details": "Деталі інтеграції", + "details": "Деталі", + "copyId": "Копіювати Id інтеграції", + "idCopiedMessage": "Id інтеграції скопійовано в буфер обміну", + "debug-mode": "Режим налагодження", + "enable-security": "Увімкнути безпеку", + "headers-filter": "Заголовки фільтра", + "header": "Заголовок", + "no-headers-filter": "Немає фільтрів заголовків", + "downlink-url": "Downlink URL", + "application-uri": "URI програми", + "as-id": "AS ID", + "as-id-required": "Необхідно вказати AS ID.", + "as-key": "AS ключ", + "as-key-required": "Необхідно вказати AS ключ.", + "max-time-diff-in-seconds": "Максимальна різниця в часі (секунди)", + "max-time-diff-in-seconds-required": "Необхідно вказати максимальну різницю в часі.", + "name": "Ім'я", + "name-required": "Необхідно вказати ім'я.", + "description": "Опис", + "base-url": "Базова URL-адреса", + "base-url-required": "Необхідно вказати базову URL-адресу", + "security-key": "Ключ захисту", + "http-endpoint": "URL кінцевої точки HTTP", + "copy-http-endpoint-url": "Скопіювати URL-адресу кінцевої точки HTTP", + "http-endpoint-url-copied-message": "URL кінцевої точки HTTP скопійовано в буфер обміну", + "host": "Хост", + "host-required": "Необхідно вказати хост.", + "host-type": "Тип хоста", + "host-type-required": "Необхідно вказати тип хоста.", + "custom-host": "Хост користувача", + "custom-host-required": "Необхідний спеціальний хост.", + "port": "Порт", + "port-required": "Необхідно вказати порт.", + "port-range": "Порт має бути в діапазоні від 1 до 65535.", + "connect-timeout": "Час очікування з'єднання (сек)", + "connect-timeout-required": " Необхідно вказати час з'єднання підключення.", + "connect-timeout-range": "Час очікування з'єднання має бути в діапазоні від 1 до 200.", + "client-id": "ID клієнта", + "clean-session": "Очистити сеанс", + "enable-ssl": "Увімкнути SSL", + "credentials": "Авторизаційні дані", + "credentials-type": "Тип авторизаційних даних", + "credentials-type-required": "Необхідно вказати тип авторизаційних даних.", + "username": "Ім'я користувача", + "username-required": "Необхідно вказати ім'я користувача.", + "password": "Пароль", + "password-required": "Необхідно вказати пароль.", + "ca-cert": "Файл сертифіката CA *", + "private-key": "Файл приватного ключа *", + "private-key-password": "Пароль приватного ключа", + "cert": "Файл сертифіката*", + "no-file": "Не вибрано жодного файлу.", + "drop-file": "Перетягніть файл, або клацніть, щоб вибрати файл для завантаження.", + "topic-filters": "Тематичні фільтри", + "remove-topic-filter": "Видалити фільтр тем", + "add-topic-filter": "Додати фільтр тем", + "add-topic-filter-prompt": "Будь ласка, додайте фільтр тем", + "topic": "Тема", + "mqtt-qos": "QoS", + "mqtt-qos-at-most-once": "Не більше одного разу", + "mqtt-qos-at-least-once": "Принаймні, один раз", + "mqtt-qos-exactly-once": "Точно один раз", + "downlink-topic-pattern": "Downlink topic pattern", + "downlink-topic-pattern-required": "Downlink topic pattern is required.", + "aws-iot-endpoint": "AWS IoT Endpoint", + "aws-iot-endpoint-required": "AWS IoT Endpoint is required.", + "aws-iot-credentials": "Авторизаційні дані AWS IoT", + "application-credentials": "Авторизаційні дані додатків", + "api-key": "API ключ", + "api-key-required": "Необхідно вказати API ключ.", + "auth-token": "Маркер аутентифікації", + "auth-token-required": "Необхідно вказати маркер аутентифікації.", + "region": "Регіон", + "region-required": "Необхідно вказати регіон.", + "application-id": "ID програми", + "application-id-required": "Необхідно вказати ID програми.", + "access-key": "Ключ доступу", + "access-key-required": "Необхідно вказати ключ доступу.", + "connection-parameters": "Параметри підключення", + "service-bus-namespace-name": "Service Bus Namespace Name", + "service-bus-namespace-name-required": "Необхідно вказати Service Bus Namespace Name is required.", + "event-hub-name": "Event Hub Name", + "event-hub-name-required": "Необхідно вказати Event Hub Name is required.", + "sas-key-name": "Назва ключа SAS", + "sas-key-name-required": "Необхідно вказати назву ключа SAS.", + "sas-key": "Ключ SAS", + "sas-key-required": "SAS Key is required.", + "iot-hub-name": "IoT Hub Name (required for downlink)", + "metadata": "Метадані", + "type": "Тип", + "type-required": "Необхідно вказати тип.", + "uplink-converter": "Конвертер передачі даних", + "uplink-converter-required": "Необхідно вказати конвертер передачі даних.", + "downlink-converter": "Downlink data converter", + "type-http": "HTTP", + "type-ocean-connect": "OceanConnect", + "type-sigfox": "SigFox", + "type-thingpark": "ThingPark", + "type-tmobile-iot-cdp": "T-Mobile – IoT CDP", + "type-mqtt": "MQTT", + "type-aws-iot": "AWS IoT", + "type-ibm-watson-iot": "IBM Watson IoT", + "type-ttn": "TheThingsNetwork", + "type-azure-event-hub": "Azure Event Hub", + "type-opc-ua": "OPC-UA", + "type-ffb": "FFB", + "opc-ua-application-name": "Назва програми", + "opc-ua-application-uri": "Application uri", + "opc-ua-scan-period-in-seconds": "Період сканування в секундах", + "opc-ua-scan-period-in-seconds-required": "Необхідно вказати період сканування в секундах", + "opc-ua-timeout": "Час очікування в мілісекундах", + "opc-ua-timeout-required": "Необхідно вказати час очікування в мілісекундах", + "opc-ua-security": "Безпека", + "opc-ua-security-required": "Необхідно задати безпеку", + "opc-ua-identity": "Ідентифікація", + "opc-ua-identity-required": "Необхідно вказати ідентифікацію", + "opc-ua-keystore": "Сховище ключів", + "add-opc-ua-keystore-prompt": "Будь ласка, додайте файл сховища ключів", + "opc-ua-keystore-required": "Необхідно вказати сховище ключів", + "opc-ua-type": "Тип", + "opc-ua-keystore-type":"Тип сховища ключів", + "opc-ua-keystore-type-required":"Необхідно вказати тип", + "opc-ua-keystore-location":"Розташування *", + "opc-ua-keystore-password":"Пароль", + "opc-ua-keystore-password-required":"Необхідно вказати пароль", + "opc-ua-keystore-alias":"Псевдонім", + "opc-ua-keystore-alias-required":"Необхідно вказати псевдонім", + "opc-ua-keystore-key-password":"Пароль ключа", + "opc-ua-keystore-key-password-required":"Необхідно вказати пароль ключа", + "opc-ua-mapping":"Зіставлення", + "add-opc-ua-mapping-prompt": "Будь ласка, додайте зіставлення", + "opc-ua-mapping-type":"Тип зіставлення", + "opc-ua-mapping-type-required":"Необхідно вказати тип зіставлення", + "opc-ua-device-node-pattern":"Шаблон вузла пристрою", + "opc-ua-device-node-pattern-required":"Необхідно вказати шаблон вузла пристрою", + "opc-ua-add-map": "Додати елемент зіставлення", + "subscription-tags": "Теги передплати Теги підписки", + "remove-subscription-tag": "Видалити тег підписки", + "add-subscription-tag": "Додати тег підписки", + "add-subscription-tag-prompt": "Будь ласка, додайте тег підписки", + "key": "Ключ", + "path": "Шлях", + "required": "Необхідно" + }, + "item": { + "selected": "Вибрані" + }, + "js-func": { + "no-return-error": "Функція повинна повертати значення!", + "return-type-mismatch": "Функція повинна повернути значення типу '{{type}}'!", + "tidy": "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": "Filter", + "type-filter-details": "Фільтрувати вхідні повідомлення з заданими умовами", + "type-enrichment": "Насичення", + "type-enrichment-details": "Додати додаткову інформацію до метаданих повідомлень", + "type-transformation": "Трансформація", + "type-transformation-details": "Змінити склад повідомлення та його метадані", + "type-action": "Дія", + "type-action-details": "Виконати задану дію", + "type-analytics": "Аналітика", + "type-analytics-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": "Допомога" + }, + "scheduler": { + "scheduler": "Планувальник", + "scheduler-event": "Scheduler event Планування події. Запланувати подію. Подія планувальника", + "select-scheduler-event": "Виберати подію", + "no-scheduler-events-matching": "Не знайдено жодних подій, які відповідають '{{entity}}'.", + "scheduler-event-required": "Необхвдно вказати заплановану подію", + "management": "Управління планувальником", + "scheduler-events": "Планування подій", + "add-scheduler-event": "Додати подію", + "search-scheduler-events": "Пошук події", + "created-time": "Час створення", + "name": "Ім'я", + "type": "Тип", + "assigned_customer": "Призначений клієнт", + "edit-scheduler-event": "Редагувати подію", + "delete-scheduler-event": "Видалити подію", + "no-scheduler-events": "Не знайдено жодних запланованих подій", + "selected-scheduler-events": "{ count, plural, 1 {1 запланована подія} other {# заплановані події} } вибрано", + "delete-scheduler-event-title": "Ви впевнені, що хочете видалити подію '{{schedulerEventName}}'?", + "delete-scheduler-event-text": "Будьте обережні, після підтвердження подія і всі пов'язані з нею дані стануть недоступними.", + "delete-scheduler-events-title": "Ви впевнені, що хочете видалити { count, plural, 1 {1 запланована подія} other {# заплановані події} }?", + "delete-scheduler-events-text": "Будьте обережні, після підтвердження всі вибрані події будуть видалені, і всі пов'язані з ними дані стануть недоступними.", + "create": "Створити подію планувальника", + "edit": "Змінити подію планувальника", + "name-required": "Необхідно задати ім'я", + "configuration": "Конфігурація", + "schedule": "Розклад", + "start": "Початок", + "date": "Дата", + "time": "Час", + "repeat": "Повтор", + "repeats": "Повтори", + "daily": "Щодня", + "weekly": "Щотижня", + "repeats-required": "Потрібно вказати повторення.", + "repeat-on": "Повторити на", + "ends-on": "Завершити на", + "sunday-label": "Нд", + "monday-label": "Пн", + "tuesday-label": "Вт", + "wednesday-label": "Ср", + "thursday-label": "Чт", + "friday-label": "Пт", + "saturday-label": "Сб", + "repeat-on-sunday": "Повторити у неділю", + "repeat-on-monday": "Повторити в понеділок", + "repeat-on-tuesday": "Повторити у вівторок", + "repeat-on-wednesday": "Повторити в середу", + "repeat-on-thursday": "Повторити в червер", + "repeat-on-friday": "Повторити в п'ятницю", + "repeat-on-saturday": "Повторити в суботу", + "event-type": "Тип події", + "select-event-type": "Вибрати тип події", + "event-type-required": "Необхідно вказати типи події.", + "list-mode": "Перегляд списку", + "calendar-mode": "Перегляд календаря", + "calendar-view-type": "Тип перегляду календаря", + "month": "Місяць", + "week": "Тиждень", + "day": "День", + "agenda-week": "Порядок тижня", + "agenda-day": "Порялок дня", + "list-year": "Список року", + "list-month": "Список місяця", + "list-week": "Список тижня", + "list-day": "Список дня", + "today": "Сьогодні", + "navigate-before": "Перейти до", + "navigate-next": "Перейти далі", + "starting-from": "Починаючи з", + "until": "до", + "on": "на", + "sunday": "Неділя", + "monday": "Понеділок", + "tuesday": "Вівторок", + "wednesday": "Середа", + "thursday": "Четвер", + "friday": "П'ятниця", + "saturday": "Субота", + "originator": "Засновник", + "single-entity": "Самостійна сутність", + "group-of-entities": "Група сутностей", + "single-device": "Один пристрій", + "group-of-devices": "Група пристроїв", + "message-body": "Тіло повідомлення", + "target": "Ціль", + "rpc-method": "Метод", + "rpc-method-required": "Необхідно вказати метод", + "rpc-params": "Парами. Парні.", + "select-dashboard-state": "Виберіть стан панелі візуалізації" + }, + "report": { + "report-config": "Конфігурація звіту", + "email-config": "Конфігурація електронної пошти", + "dashboard-state-param": "Значення параметра стану панелі візуалізації", + "base-url": "Базова URL-адреса", + "base-url-required": "Необхідно вказати базову URL-адресу.", + "use-dashboard-timewindow": "Використовуйте вікно часу на панелі інструментів", + "timewindow": "Вікно часу", + "name-pattern": "Шаблон імені звіту", + "name-pattern-required": "Необхідно задати шаблон назви звіту", + "type": "Report type", + "use-current-user-credentials": "Використовувати поточні авторизаційні дані користувача", + "customer-user-credentials": "Авторизаційні дані користувачів", + "customer-user-credentials-required": "Необхідно задати авторизаційні дані користувачів", + "generate-test-report": "Створити звіт про перевірку", + "send-email": "Відправити лист", + "from": "Від", + "from-required": "Необхідно вказати від кого.", + "to": "До", + "to-required": "Необхідно вказати до кого.", + "cc": "Cc", + "bcc": "Bcc", + "subject": "Тема", + "subject-required": "Необхідно вказати тему.", + "body": "Тіло", + "body-required": "Необідно вказати тіло." + }, + "blob-entity": { + "blob-entity": "Blob сутності", + "select-blob-entity": "Вибрати blob сутності", + "no-blob-entities-matching": "Не знайдено жодних сутностей blob, які відповідають '{{entity}}'.", + "blob-entity-required": "Необхідно вказати blob сутності", + "files": "Файли", + "search": "Пошук файлів", + "clear-search": "Очистити пошук", + "no-blob-entities-prompt": "Файлів не знайдено", + "report": "Звіт", + "created-time": "Створено час", + "name": "Ім'я", + "type": "Тип", + "assigned_customer": "Призначений клієнт", + "download-blob-entity": "Завантажити файл", + "delete-blob-entity": "Видалити файл", + "delete-blob-entity-title": "Ви впевнені, що хочете видалити файл '{{blobEntityName}}'?", + "delete-blob-entity-text": "Будьте обережні, після підствердження, дані файлу стануть недоступними." + }, + "timezone": { + "timezone": "Часовий пояс", + "select-timezone": "Виберати часовий пояс ", + "no-timezones-matching": "Не знайдено жодних часових поясів, які відповідають '{{timezone}}'.", + "timezone-required": "Необхідно вказати часовий пояс." + }, + "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": "Необхідно вказати власника", + "selected-tenants": "{ count, plural, 1 {1 власник} other {# власники} } вибрано", + "search": "Пошук власників" + }, + "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": "з {{ startTime }} до {{ endTime }}", + "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": "Для активувації користувача, скористайтеся наступним activation link :", + "copy-activation-link": "Скопіювати активаційне посилання ", + "activation-link-copied-message": "Посилання на активацію користувача було скопійовано в буфер обміну", + "selected-users": "{ count, plural, 1 {1 користувач} other {# користувачі} } вибрано", + "search": "Пошук користувачів", + "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 тип віджета було видалено.", + "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": "Експртувати віджет", + "export-data": "Експортувати дані віджетів", + "export-to-csv": "Експортувати дані в CSV...", + "export-to-excel": "Експортувати дані в XLS..." + }, + "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 {пакет віджетів} 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": "Увімкнути повноекранний режим", + "enable-data-export": "Увімкнути експорт даних", + "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": "Дія з такою назвою вже існує.
Назва дії має бути унікальною в межах одного джерела дії.", + "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": "Неможливо імпортувати тип віджету: неправильна структура даних типу віджета." + }, + "white-labeling": { + "white-labeling": "Білий маркування", + "login-white-labeling": "Login White Labeling", + "preview": "Попередній перегляд", + "app-title": "Назва програми", + "favicon": "Іконка веб-сайту", + "favicon-description": "*.ico, *.gif or *.png image with maximum size {{kbSize}} KBytes.", + "favicon-size-error": "Зображення веб-сайту завелике. Максимально дозволений розмір зображення веб-сайту {{kbSize}} KBytes.", + "favicon-type-error": "Недійсний формат файлу зображення веб-сайту. Приймаються лише зображення ICO, GIF або PNG.", + "drop-favicon-image": "Зніміть зображення піктограми веб-сайту або клацніть, щоб вибрати файл для завантаження.", + "no-favicon-image": "Не вибрано жодної іконки", + "logo": "Логотип", + "logo-description": "Будь-яке зображення з максимальним розміром {{kbSize}} KBytes.", + "logo-size-error": "Зображення логотипу занадто велике. Максимально дозволений розмір зображення логотипу{{kbSize}} KBytes.", + "logo-type-error": "Недійсний формат файлу логотипу. Приймаються тільки зображення.", + "drop-logo-image": "Зніміть зображення логотипу або клацніть, щоб вибрати файл для завантаження.", + "no-logo-image": "Не вибрано жожного логотипу", + "logo-height": "Висота логотипу, px", + "primary-palette": "Основна палітра", + "accent-palette": "Палітра акцент", + "customize-palette": "Налаштування", + "edit-palette": "Редагувати палітру", + "save-palette": "Зберегти палітру", + "primary-background": "Первинний фон", + "secondary-background": "Вторинний фон", + "hue1": "HUE 1", + "hue2": "HUE 2", + "hue3": "HUE 3", + "page-background-color": "Колір фону сторінки", + "dark-foreground": "Темний передній план", + "domain-name": "Доменне ім'я" + }, + "icon": { + "icon": "веб-іконка", + "select-icon": "Виберіть веб-іконку", + "material-icons": "Матеріал веб-іконки", + "show-all": "Показати всі веб-іконки" + }, + "custom": { + "widget-action": { + "action-cell-button": "Кнопка дії клітинки", + "row-click": "Клацніть на рядок", + "marker-click": "Клацніть на маркер", + "tooltip-tag-action": "Дії при підказці" + } + }, + "language": { + "language": "Мова", + "locales": { + "fr_FR": "Французька", + "zh_CN": "Китайська", + "en_US": "Англійська", + "it_IT": "Італійська", + "ko_KR": "Корейська", + "ru_RU": "Російська", + "es_ES": "Іспанська", + "ja_JA": "Японська", + "tr_TR": "Турецька", + "de_DE": "Німецька", + "uk_UA": "Українська", + "fa_IR": "Перська" + } + } +} diff --git a/ui/src/app/locale/locale.constant-zh_CN.json b/ui/src/app/locale/locale.constant-zh_CN.json index eaf810c986..41089cf611 100644 --- a/ui/src/app/locale/locale.constant-zh_CN.json +++ b/ui/src/app/locale/locale.constant-zh_CN.json @@ -589,11 +589,11 @@ "manage-credentials": "管理凭据", "delete": "删除设备", "assign-devices": "分配设备", - "assign-devices-text": "将{count,select,1 {1 设备} other {# 设备}}分配给客户", + "assign-devices-text": "将{count,plural,1 {1 设备} other {# 设备}}分配给客户", "delete-devices": "删除设备", "unassign-from-customer": "取消分配客户", "unassign-devices": "取消分配设备", - "unassign-devices-action-title": "从客户处取消分配{count,select,1 {1 设备} other {# 设备}}", + "unassign-devices-action-title": "从客户处取消分配{count,plural,1 {1 设备} other {# 设备}}", "assign-new-device": "分配新设备", "make-public-device-title": "您确定要将设备 '{{deviceName}}' 设为公开吗?", "make-public-device-text": "确认后,设备及其所有数据将被设为公开并可被其他人访问。", @@ -602,13 +602,13 @@ "view-credentials": "查看凭据", "delete-device-title": "您确定要删除设备的{{deviceName}}吗?", "delete-device-text": "小心!确认后设备及其所有相关数据将不可恢复。", - "delete-devices-title": "您确定要删除{count,select,1 {1 设备} other {# 设备}} 吗?", - "delete-devices-action-title": "删除 {count,select,1 {1 设备} other {# 设备}}", + "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,select,1 {1 设备} other {# 设备}} 吗?", + "unassign-devices-title": "您确定要取消分配{count,plural,1 {1 设备} other {# 设备}} 吗?", "unassign-devices-text": "确认后,所有选定的设备将被取消分配,并且客户将无法访问。", "device-credentials": "设备凭据", "credentials-type": "凭据类型", @@ -1176,7 +1176,7 @@ "tenant-details": "租客详情", "delete-tenant-title": "您确定要删除租户'{{tenantTitle}}'吗?", "delete-tenant-text": "小心!确认后,租户和所有相关数据将不可恢复。", - "delete-tenants-title": "您确定要删除 {count,select,1 {1 租户} other {# 租户}} 吗?", + "delete-tenants-title": "您确定要删除 {count,plural,1 {1 租户} other {# 租户}} 吗?", "delete-tenants-action-title": "删除 { count, plural, 1 {1 租户} other {# 租户} }", "delete-tenants-text": "小心!确认后,所有选定的租户将被删除,所有相关数据将不可恢复。", "title": "标题", @@ -1446,7 +1446,8 @@ "it_IT": "意大利", "ja_JA": "日本", "tr_TR": "土耳其", - "fa_IR": "波斯语" + "fa_IR": "波斯语", + "uk_UA": "乌克兰" } } } \ No newline at end of file From bc7db03351957b2ed410c1d0c918bfb9ca43b833 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Fri, 29 Mar 2019 13:50:52 +0200 Subject: [PATCH 12/12] Improve GRPC session error handling. --- .../service/cluster/rpc/GrpcSession.java | 19 ++++++++++++++----- pom.xml | 4 ++-- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/cluster/rpc/GrpcSession.java b/application/src/main/java/org/thingsboard/server/service/cluster/rpc/GrpcSession.java index b5deda2edd..55b83e8ad9 100644 --- a/application/src/main/java/org/thingsboard/server/service/cluster/rpc/GrpcSession.java +++ b/application/src/main/java/org/thingsboard/server/service/cluster/rpc/GrpcSession.java @@ -95,15 +95,24 @@ public final class GrpcSession implements Closeable { } public void sendMsg(ClusterAPIProtos.ClusterMessage msg) { - outputStream.onNext(msg); - } - - public void onError(Throwable t) { - outputStream.onError(t); + if (connected) { + try { + outputStream.onNext(msg); + } catch (Throwable t) { + try { + outputStream.onError(t); + } catch (Throwable t2) { + } + listener.onError(GrpcSession.this, t); + } + } else { + log.warn("[{}] Failed to send message due to closed session!", sessionId); + } } @Override public void close() { + connected = false; try { outputStream.onCompleted(); } catch (IllegalStateException e) { diff --git a/pom.xml b/pom.xml index 59cc98aef0..67a2eef187 100755 --- a/pom.xml +++ b/pom.xml @@ -61,10 +61,10 @@ 1.4.3 4.2.0 3.6.1 - 1.16.1 + 1.19.0 1.16.18 1.1.0 - 4.1.30.Final + 4.1.34.Final 1.5.0 4.8.0 2.19.1